Как клонировать ArrayList, а также клонировать его содержимое?

Как я могу клонировать ArrayList, а также клонировать его элементы в Java?

Например, у меня есть:

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = ....something to do with dogs....

И я ожидал бы, что объекты в clonedList не совпадают с объектами в списке собак.

Ответ 1

Вам нужно будет перебирать элементы и клонировать их один за другим, помещая клоны в массив результатов по мере продвижения.

public static List<Dog> cloneList(List<Dog> list) {
    List<Dog> clone = new ArrayList<Dog>(list.size());
    for (Dog item : list) clone.add(item.clone());
    return clone;
}

Очевидно, что для того, чтобы это работало, вам нужно заставить свой класс Dog реализовать интерфейс Cloneable и переопределить метод clone().

Ответ 2

Я лично добавлю конструктор в Dog:

class Dog
{
    public Dog()
    { ... } // Regular constructor

    public Dog(Dog dog) {
        // Copy all the fields of Dog.
    }
}

Затем просто повторите (как показано в ответе Вархана):

public static List<Dog> cloneList(List<Dog> dogList) {
    List<Dog> clonedList = new ArrayList<Dog>(dogList.size());
    for (Dog dog : dogList) {
        clonedList.add(new Dog(dog));
    }
    return clonedList;
}

Я нахожу преимущество этого: вам не нужно вкручиваться с разбитым клонированным материалом на Java. Он также соответствует тому, как вы копируете коллекции Java.

Другой вариант - написать собственный интерфейс ICloneable и использовать его. Таким образом, вы можете написать общий метод клонирования.

Ответ 3

Все стандартные коллекции имеют конструкторы копий. Используй их.

List<Double> original = // some list
List<Double> copy = new ArrayList<Double>(original); //This does a shallow copy

clone() был разработан с несколькими ошибками (см. этот вопрос), поэтому лучше его избегать.

Из Эффективной Java, 2-е издание, пункт 11: разумно переопределить клон

Учитывая все проблемы, связанные с Cloneable, можно с уверенностью сказать, что другие интерфейсы не должны расширять его, и что классы, предназначенные для наследования (элемент 17), не должны его реализовывать. Из-за множества недостатков некоторые опытные программисты просто предпочитают никогда не переопределять метод клонирования и никогда не вызывать его, за исключением, возможно, копирования массивов. Если вы разрабатываете класс для наследования, имейте в виду, что если вы решите не предоставлять хорошо защищенный метод клонирования, для подклассов будет невозможно реализовать Cloneable.

Эта книга также описывает множество преимуществ конструкторов копирования по сравнению с Cloneable/clone.

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

Рассмотрим еще одно преимущество использования конструкторов копирования. Предположим, у вас есть HashSet s, и вы хотите скопировать его как TreeSet. Метод clone не может предложить эту функциональность, но его легко с помощью конструктора преобразования: new TreeSet(s).

Ответ 4

Java 8 предоставляет новый способ элегантного и компактного вызова конструктора копирования или метода клонирования для собак-элементов: потоки, лямбды и сборщики.

Скопируйте конструктор:

List<Dog> clonedDogs = dogs.stream().map(Dog::new).collect(toList());

Выражение Dog::new называется ссылкой на метод. Он создает объект функции, который вызывает конструктор для Dog который принимает в качестве аргумента другую собаку.

Метод клонирования [1]:

List<Dog> clonedDogs = dogs.stream().map(d -> d.clone()).collect(toList());

Получение ArrayList в результате

Или, если вам нужно вернуть ArrayList (на случай, если вы захотите изменить его позже):

ArrayList<Dog> clonedDogs = dogs.stream().map(Dog::new).collect(toCollection(ArrayList::new));

Обновите список на месте

Если вам не нужно сохранять исходное содержимое списка dogs вы можете вместо этого использовать метод replaceAll и обновить список на месте:

dogs.replaceAll(Dog::new);

Все примеры предполагают import static java.util.stream.Collectors.*; ,


Коллектор для ArrayList s

Сборщик из последнего примера может быть превращен в метод util. Поскольку это настолько распространенная вещь, мне лично нравится, чтобы она была короткой и красивой. Как это:

ArrayList<Dog> clonedDogs = dogs.stream().map(d -> d.clone()).collect(toArrayList());

public static <T> Collector<T, ?, ArrayList<T>> toArrayList() {
    return Collectors.toCollection(ArrayList::new);
}

[1] Примечание по CloneNotSupportedException:

Чтобы это решение работало, метод clone Dog не должен объявлять, что он CloneNotSupportedException. Причина в том, что аргумент для map не может генерировать какие-либо проверенные исключения.

Как это:

    // Note: Method is public and returns Dog, not Object
    @Override
    public Dog clone() /* Note: No throws clause here */ { ...

Это не должно быть большой проблемой, так как в любом случае это лучшая практика. (Effectice Java, например, дает этот совет.)

Спасибо Густаво за то, что заметил это.


PS:

Если вы найдете это красивее, вы можете вместо этого использовать синтаксис ссылки на метод, чтобы сделать то же самое:

List<Dog> clonedDogs = dogs.stream().map(Dog::clone).collect(toList());

Ответ 5

В принципе существует три способа без повторения вручную,

1 Используя конструктор

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>(dogs);

2 Используя addAll(Collection<? extends E> c)

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>();
clonedList.addAll(dogs);

3 Используя метод addAll(int index, Collection<? extends E> c) с параметром int

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>();
clonedList.addAll(0, dogs);

Примечание. Поведение этих операций будет undefined, если указанная коллекция будет изменена во время выполнения операции.

Ответ 6

Я думаю, что текущий зеленый ответ плох, почему вы можете спросить?

  • Может потребоваться добавить много кода
  • Он требует, чтобы вы перечислили все списки, которые нужно скопировать, и сделайте это

Способ сериализации также является плохим imo, возможно, вам придется добавить Serializable повсюду.

Итак, какое решение:

Библиотека глубокого клонирования Java Библиотека клонирования - небольшая библиотека Java с открытым исходным кодом (apache license), которая глубоко клонирует объекты. Объектам не нужно реализовывать интерфейс Cloneable. Эффективно, эта библиотека может клонировать ЛЮБЫЕ java-объекты. Его можно использовать, то есть в реализациях кеша, если вы не хотите, чтобы кешированный объект был изменен или когда вы хотите создать глубокую копию объектов.

Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);

Проверьте это на https://github.com/kostaskougios/cloning

Ответ 7

Вам нужно будет клонировать ArrayList вручную (путем итерации по нему и копирования каждого элемента в новый ArrayList), потому что clone() не сделает это за вас. Причина этого в том, что объекты, содержащиеся в ArrayList, могут не реализовывать сами Clonable.

Изменить:... и это именно то, что делает код Varkhan.

Ответ 8

Я нашел способ, вы можете использовать json для сериализации /unserialize списка. Сериализованный список не ссылается на исходный объект при несериализации.

Использование gson:

List<CategoryModel> originalList = new ArrayList<>(); // add some items later
String listAsJson = gson.toJson(originalList);
List<CategoryModel> newList = new Gson().fromJson(listAsJson, new TypeToken<List<CategoryModel>>() {}.getType());

Вы можете сделать это с помощью jackson и любой другой библиотеки json.

Ответ 9

Отвратительный способ - сделать это с отражением. Что-то вроде этого сработало для меня.

public static <T extends Cloneable> List<T> deepCloneList(List<T> original) {
    if (original == null || original.size() < 1) {
        return new ArrayList<>();
    }

    try {
        int originalSize = original.size();
        Method cloneMethod = original.get(0).getClass().getDeclaredMethod("clone");
        List<T> clonedList = new ArrayList<>();

        // noinspection ForLoopReplaceableByForEach
        for (int i = 0; i < originalSize; i++) {
            // noinspection unchecked
            clonedList.add((T) cloneMethod.invoke(original.get(i)));
        }
        return clonedList;
    } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
        System.err.println("Couldn't clone list due to " + e.getMessage());
        return new ArrayList<>();
    }
}

Ответ 11

Другие плакаты верны: вам нужно перебирать список и копировать в новый список.

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

Другим преимуществом неизменности является то, что они также являются потокобезопасными.

Ответ 12

Вот решение, использующее общий тип шаблона:

public static <T> List<T> copyList(List<T> source) {
    List<T> dest = new ArrayList<T>();
    for (T item : source) { dest.add(item); }
    return dest;
}

Ответ 13

для объектов override метод clone()

class You_class {

    int a;

    @Override
    public You_class clone() {
        You_class you_class = new You_class();
        you_class.a = this.a;
        return you_class;
    }
}

и вызовите .clone() для объекта Vector obj или ArraiList obj....

Ответ 14

Простой способ, используя commons-lang-2.3.jar, что библиотека java для клонирования списка

link скачать commons-lang-2.3.jar

Как использовать

oldList.........
List<YourObject> newList = new ArrayList<YourObject>();
foreach(YourObject obj : oldList){
   newList.add((YourObject)SerializationUtils.clone(obj));
}

Надеюсь, это поможет.

: D

Ответ 15

Пакет import org.apache.commons.lang.SerializationUtils;

Существует метод SerializationUtils.clone(Object);

Пример

this.myObjectCloned = SerializationUtils.clone(this.object);

Ответ 16

Я только что создал lib, способный клонировать объект entity и объект java.util.List. Просто загрузите банку в https://drive.google.com/open?id=0B69Sui5ah93EUTloSktFUkctN0U и используйте статический метод cloneListObject (Список списков). Этот метод не только клонирует список, но также и все элементы сущности.

Ответ 17

Я использовал эту опцию всегда:

ArrayList<Dog> clonedList = new ArrayList<Dog>(name_of_arraylist_that_you_need_to_Clone);

Ответ 18

Я думаю, что нашел очень простой способ сделать глубокую копию ArrayList. Предполагая, что вы хотите скопировать массив String ArrayList.

ArrayList<String>arrayB = new ArrayList<String>();
arrayB.addAll(arrayA);

Сообщите мне, если это не сработает для вас.