Как создать копию ArrayList в Java: методы и особенности

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Создать точную копию ArrayList в Java можно при помощи выражения new ArrayList<>(originalList). В результате вы получите новый объект ArrayList, содержащий те же элементы, что и исходный список.

Если ваш список состоит из объектов, то под копированием подразумевается дублирование ссылок на эти объекты, а не сами объекты. Таким образом, копия и оригинал будут ссылаться на одни и те же экземпляры объектов.

Java
Скопировать код
ArrayList<String> copiedList = new ArrayList<>(originalList);

Для выполнения глубокой копии, которая подразумевает клонирование объктов, потребуется ручной подход, поскольку метод clone() у ArrayList не предполагает копирование содержимого объектов.

Кинга Идем в IT: пошаговый план для смены профессии

Виды копирования: поверхностное и глубокое

Поверхностное копирование: clone()

Альтернативой создания поверхностной копии является использование метода clone(), который клонирует структуру списка, но не клонирует содержащиеся в нём объекты.

Java
Скопировать код
@SuppressWarnings("unchecked")
ArrayList<String> clonedList = (ArrayList<String>) originalList.clone();

Примечание: Использование метода clone() требует особой осторожности, поскольку он не является типобезопасным. Неправильное применение данного метода может вызвать ClassCastException при выполнении.

Поверхностное копирование: addAll()

Метод addAll() позволяет дополнить один список всеми элементами другого списка, и это один из способов проведения поверхностного копирования.

Java
Скопировать код
ArrayList<String> listToAdd = new ArrayList<>();
listToAdd.addAll(originalList); // Элементы успешно добавлены без каких-либо ухищрений!

Поверхностное копирование: Collections.copy()

При использовании Collections.copy() необходимо предварительно инициализировать целевой список достаточным размером. Это можно сделать при помощи Collections.nCopies().

Java
Скопировать код
List<String> destList = new ArrayList<>(Collections.nCopies(originalList.size(), (String)null)); // Сначала мы заполняем список ссылками-заглушками.
Collections.copy(destList, originalList); // И вуаля, полная копия готова.

Глубокое копирование: вручную, через Cloneable или с использованием сериализации

Создание глубокой копии потребует больше усилий:

  • Проход по каждому элементу с его последующим копированием.
  • Использование конструктора копирования или метода clone, если объекты поддерживают интерфейс Cloneable.
  • Применение сериализации.
Java
Скопировать код
// Пример глубокого копирования путем ручного копирования элементов, поддерживающих интерфейс Cloneable
ArrayList<MyObject> deepCopiedList = new ArrayList<>();
for (MyObject item : originalList) {
    deepCopiedList.add(item.clone()); // Каждый объект клонируется отдельно.
}

Визуализация

Представьте процесс копирования ArrayList следующим образом – работа ксерокса:

Java
Скопировать код
ArrayList<String> originalList = new ArrayList<>(Arrays.asList("🍏", "🍊", "🍇"));
ArrayList<String> copyList = new ArrayList<>(originalList);

Процесс копирования:

Markdown
Скопировать код
Оригинальный список:  [🍏, 🍊, 🍇]
🖨️ ==> [🍏, 🍊, 🍇]
Скопированный список: [🍏, 🍊, 🍇]

Теперь у нас два списка с одинаковым содержимым. Но при поверхностном копировании оба списка делят элементы между собой.

Изменяемые элементы в ArrayList

Проблема изменяемых объектов

При поверхностном копировании всех содержащихся изменяемых объектов, любые модификации в любом из списков будут отражены и в другом списке из-за общих ссылок на объекты. Понимание типов данных и их изменяемости (например, StringBuilder) или неизменяемости (например, String) является крайне важным.

Глубокое копирование как решение

При работе с изменяемыми объектами в ArrayList, разумно применять глубокое копирование. Оно обеспечивает сохранение данных и предотвращает случайные изменения, которые могут вызвать сложности при поиске ошибок.

Для вложенных коллекций требуется вложенное копирование

ArrayList, содержащий другие коллекции (например, ArrayList<ArrayList<String>>), требует копирования как внешнего, так и каждого из внутренних списков для создания полной независимой копии.

Важность производительности при копировании

Время и производительность

И поверхностное, и глубокое копирование требуют времени. Поверхностное копирование занимает время O(n), в то время как глубокое копирование может занять намного больше времени, в зависимости от сложности объектов.

Баланс между скоростью и расходом памяти

Глубокая копия защищает от неожиданных ошибок, но требует больших запасов памяти, поскольку создаются новые экземпляры каждого элемента. Поверхностная же копия более экономична по памяти, так как использует уже существующие объекты.

Полезные материалы

  1. ArrayList (Java Platform SE 8) — официальная документация по ArrayList.
  2. java – How to clone ArrayList and also clone its contents? – Stack Overflow — обсуждение вопроса глубокого копирования ArrayList.
  3. java – How to properly override clone method? – Stack Overflow — рекомендации по переопределению метода clone.
  4. DigitalOcean Tutorial — обучающий материал по интерфейсу Cloneable в Java.
  5. The Java™ Tutorials — понятные и подробные уроки по работе с коллекциями в Java.
  6. disassociate 2 domain objects (Groovy forum at Coderanch) — объяснения о разделении доменных объектов, среди которых и коллекции.