1. Обзор
В Java создание копии списка
иногда может привести к исключению IndexOutOfBoundsException: «Источник не соответствует назначению».
В этом кратком руководстве мы рассмотрим, почему мы получаем эту ошибку при использовании метода Collections.copy
и как ее можно решить. Мы также рассмотрим альтернативы Collections.copy
для создания копии списка.
2. Воспроизведение проблемы
Начнем с метода создания копии списка
с помощью метода Collections.copy
:
static List<Integer> copyList(List<Integer> source) {
List<Integer> destination = new ArrayList<>(source.size());
Collections.copy(destination, source);
return destination;
}
Здесь метод copyList
создает новый список с начальной емкостью , равной размеру исходного списка. Затем он пытается скопировать элементы исходного списка в список назначения:
List<Integer> source = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> copy = copyList(source);
Однако, как только мы вызываем метод copyList
, он генерирует исключение java.lang.IndexOutOfBoundsException: Source not fit in dest
.
3. Причина исключения
Попробуем понять, что пошло не так. Согласно документации для метода Collections.copy :
> Список назначения должен быть не меньше исходного списка. Если он длиннее, остальные элементы в списке назначения не затрагиваются.
В нашем примере мы создали новый список
с помощью конструктора с начальной емкостью, равной размеру исходного списка. Он просто выделяет достаточно памяти и фактически не определяет элементы. Размер нового списка остается нулевым, поскольку емкость и размер являются разными атрибутами списка
.
Таким образом, когда метод Collections.copy
пытается скопировать исходный список в целевой, он создает исключение java.lang.IndexOutOfBoundsException.
4. Решения
4.1. Коллекции.copy
Давайте рассмотрим рабочий пример копирования списка
в другой список
с помощью метода Collections.copy
:
List<Integer> destination = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> source = Arrays.asList(11, 22, 33);
Collections.copy(destination, source);
В данном случае мы копируем все три элемента исходного списка в целевой список. Метод Arrays.asList
инициализирует список элементами, а не только размером, поэтому мы можем успешно скопировать исходный список в целевой список.
Если мы просто поменяем местами аргументы метода Collections.copy
, он выдаст исключение java.lang.IndexOutOfBoundsException
, поскольку размер исходного списка меньше размера целевого списка .
После этой операции копирования список назначения выглядит так:
[11, 22, 33, 4, 5]
Наряду с методом Collections.copy
в Java есть и другие способы сделать копию List
. Давайте посмотрим на некоторые из них.
4.2. Конструктор ArrayList
Самый простой способ скопировать список
— использовать конструктор, который принимает параметр Collection
:
List<Integer> source = Arrays.asList(11, 22, 33);
List<Integer> destination = new ArrayList<>(source);
Здесь мы просто передаем исходный список конструктору целевого списка, который создает поверхностную копию исходного списка.
Список назначения будет просто еще одной ссылкой на тот же объект, на который ссылается исходный список. Таким образом, каждое изменение, сделанное любой ссылкой, повлияет на один и тот же объект.
Поэтому использование конструктора — хороший вариант для копирования неизменяемых объектов, таких как целые числа
и строки.
4.3. добавить все
Другой простой способ — использовать метод addAll
класса List
:
List<Integer> destination = new ArrayList<>();
destination.addAll(source);
Метод addAll
скопирует все элементы исходного списка в целевой список.
Есть несколько замечаний относительно этого подхода:
- Он создает поверхностную копию исходного списка.
- Элементы исходного списка добавляются к целевому списку.
4.4. Потоки
Java 8 ``
Java 8 представила Stream API , который является отличным инструментом для работы с коллекциями Java.
С помощью метода stream()
мы делаем копию списка с помощью Stream API :
List<Integer> copy = source.stream()
.collect(Collectors.toList());
4.5. Ява 10
Копирование списка
в Java 10 еще проще. Использование метода copyOf()
позволяет нам создать неизменяемый список, содержащий элементы данной коллекции
:
List<Integer> destination = List.copyOf(sourceList);
Если мы хотим использовать этот подход, нам нужно убедиться, что входной список
не является нулевым
и что он не содержит пустых
элементов.
5. Вывод
В этой статье мы рассмотрели, как и почему метод Collections.copy
выдает IndexOutOfBoundException «Source not file in dest»
. Наряду с этим мы также изучили различные способы копирования списка
в другой список.
И примеры до Java-10, и примеры для Java 10 можно найти на GitHub.