Как создать копию ArrayList в Java: методы и особенности
Быстрый ответ
Создать точную копию ArrayList
в Java можно при помощи выражения new ArrayList<>(originalList)
. В результате вы получите новый объект ArrayList
, содержащий те же элементы, что и исходный список.
Если ваш список состоит из объектов, то под копированием подразумевается дублирование ссылок на эти объекты, а не сами объекты. Таким образом, копия и оригинал будут ссылаться на одни и те же экземпляры объектов.
ArrayList<String> copiedList = new ArrayList<>(originalList);
Для выполнения глубокой копии, которая подразумевает клонирование объктов, потребуется ручной подход, поскольку метод clone()
у ArrayList
не предполагает копирование содержимого объектов.
Виды копирования: поверхностное и глубокое
Поверхностное копирование: clone()
Альтернативой создания поверхностной копии является использование метода clone()
, который клонирует структуру списка, но не клонирует содержащиеся в нём объекты.
@SuppressWarnings("unchecked")
ArrayList<String> clonedList = (ArrayList<String>) originalList.clone();
Примечание: Использование метода clone()
требует особой осторожности, поскольку он не является типобезопасным. Неправильное применение данного метода может вызвать ClassCastException
при выполнении.
Поверхностное копирование: addAll()
Метод addAll()
позволяет дополнить один список всеми элементами другого списка, и это один из способов проведения поверхностного копирования.
ArrayList<String> listToAdd = new ArrayList<>();
listToAdd.addAll(originalList); // Элементы успешно добавлены без каких-либо ухищрений!
Поверхностное копирование: Collections.copy()
При использовании Collections.copy()
необходимо предварительно инициализировать целевой список достаточным размером. Это можно сделать при помощи Collections.nCopies()
.
List<String> destList = new ArrayList<>(Collections.nCopies(originalList.size(), (String)null)); // Сначала мы заполняем список ссылками-заглушками.
Collections.copy(destList, originalList); // И вуаля, полная копия готова.
Глубокое копирование: вручную, через Cloneable
или с использованием сериализации
Создание глубокой копии потребует больше усилий:
- Проход по каждому элементу с его последующим копированием.
- Использование конструктора копирования или метода
clone
, если объекты поддерживают интерфейсCloneable
. - Применение сериализации.
// Пример глубокого копирования путем ручного копирования элементов, поддерживающих интерфейс Cloneable
ArrayList<MyObject> deepCopiedList = new ArrayList<>();
for (MyObject item : originalList) {
deepCopiedList.add(item.clone()); // Каждый объект клонируется отдельно.
}
Визуализация
Представьте процесс копирования ArrayList
следующим образом – работа ксерокса:
ArrayList<String> originalList = new ArrayList<>(Arrays.asList("🍏", "🍊", "🍇"));
ArrayList<String> copyList = new ArrayList<>(originalList);
Процесс копирования:
Оригинальный список: [🍏, 🍊, 🍇]
🖨️ ==> [🍏, 🍊, 🍇]
Скопированный список: [🍏, 🍊, 🍇]
Теперь у нас два списка с одинаковым содержимым. Но при поверхностном копировании оба списка делят элементы между собой.
Изменяемые элементы в ArrayList
Проблема изменяемых объектов
При поверхностном копировании всех содержащихся изменяемых объектов, любые модификации в любом из списков будут отражены и в другом списке из-за общих ссылок на объекты. Понимание типов данных и их изменяемости (например, StringBuilder
) или неизменяемости (например, String
) является крайне важным.
Глубокое копирование как решение
При работе с изменяемыми объектами в ArrayList
, разумно применять глубокое копирование. Оно обеспечивает сохранение данных и предотвращает случайные изменения, которые могут вызвать сложности при поиске ошибок.
Для вложенных коллекций требуется вложенное копирование
ArrayList
, содержащий другие коллекции (например, ArrayList<ArrayList<String>>
), требует копирования как внешнего, так и каждого из внутренних списков для создания полной независимой копии.
Важность производительности при копировании
Время и производительность
И поверхностное, и глубокое копирование требуют времени. Поверхностное копирование занимает время O(n), в то время как глубокое копирование может занять намного больше времени, в зависимости от сложности объектов.
Баланс между скоростью и расходом памяти
Глубокая копия защищает от неожиданных ошибок, но требует больших запасов памяти, поскольку создаются новые экземпляры каждого элемента. Поверхностная же копия более экономична по памяти, так как использует уже существующие объекты.
Полезные материалы
- ArrayList (Java Platform SE 8) — официальная документация по
ArrayList
. - java – How to clone ArrayList and also clone its contents? – Stack Overflow — обсуждение вопроса глубокого копирования
ArrayList
. - java – How to properly override clone method? – Stack Overflow — рекомендации по переопределению метода
clone
. - DigitalOcean Tutorial — обучающий материал по интерфейсу
Cloneable
в Java. - The Java™ Tutorials — понятные и подробные уроки по работе с коллекциями в Java.
- disassociate 2 domain objects (Groovy forum at Coderanch) — объяснения о разделении доменных объектов, среди которых и коллекции.