«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: Источник не соответствует целевому .
3. Причина исключения
Попробуем разобраться, что пошло не так. Согласно документации для метода Collections.copy:
The destination list must be at least as long as the source list. If it’s longer, the remaining elements in the destination list are unaffected.
В нашем примере мы создали новый список с помощью конструктора с начальной емкостью, равной размеру исходного списка. Он просто выделяет достаточно памяти и фактически не определяет элементы. Размер нового списка остается нулевым, поскольку емкость и размер — это разные атрибуты списка.
Таким образом, когда метод Collections.copy пытается скопировать исходный список в целевой, он генерирует исключение java.lang.IndexOutOfBoundsException.
4. Решения
4.1. Collections.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
Еще один простой способ — использовать метод addAll класса List:
List<Integer> destination = new ArrayList<>();
destination.addAll(source);
Метод addAll скопирует все элементы исходного списка в целевой список.
Есть несколько замечаний относительно этого подхода:
- It creates a shallow copy of the source list.
- The elements of the source list are appended to the destination list.
4.4. Java 8 Streams
В Java 8 появился Stream API, который является отличным инструментом для работы с Java-коллекциями.
С помощью метода stream() делаем копию списка с помощью Stream API:
List<Integer> copy = source.stream()
.collect(Collectors.toList());
4.5. Java 10
Копирование списка еще проще в Java 10. Использование метода copyOf() позволяет нам создать неизменяемый список, содержащий элементы данной коллекции:
List<Integer> destination = List.copyOf(sourceList);
Если мы хотим использовать этот подход , нам нужно убедиться, что входной список не является нулевым и не содержит пустых элементов.
5. Заключение
В этой статье мы рассмотрели, как и почему метод Collections.copy выдает исключение IndexOutOfBoundException «Source not file in dest». Наряду с этим мы также изучили различные способы копирования списка в другой список.
И примеры до Java-10, и примеры для Java 10 можно найти на GitHub.