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

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

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

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

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

+238
03 апр. '09 в 20:40
источник поделиться
18 ответов

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

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().

+179
03 апр. '09 в 20:43
источник

Связанные вопросы


Похожие вопросы

Я лично добавлю конструктор в 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 и использовать его. Таким образом, вы можете написать общий метод клонирования.

+177
03 апр. '09 в 22:02
источник

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

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).

+126
10 апр. '12 в 4:30
источник

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());
+35
03 нояб. '15 в 19:36
источник

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

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, если указанная коллекция будет изменена во время выполнения операции.

+28
18 февр. '15 в 13:05
источник

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

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

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

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

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

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

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

+18
07 авг. '09 в 5:58
источник

Я нашел способ, вы можете использовать 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.

+2
09 июн. '16 в 21:44
источник

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

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

+2
03 апр. '09 в 20:46
источник

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

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<>();
    }
}
+1
01 окт. '15 в 20:59
источник

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

ArrayList<Dog> clonedList = new ArrayList<Dog>(name_of_arraylist_that_you_need_to_Clone);
0
18 мая '18 в 7:20
источник

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

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

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

0
04 апр. '09 в 10:12
источник

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

0
11 мая '17 в 23:28
источник

Простой способ, используя 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

0
04 апр. '13 в 16:20
источник

для объектов 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....

0
10 сент. '12 в 13:45
источник

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

public static <T> List<T> copyList(List<T> source) {
    List<T> dest = new ArrayList<T>();
    for (T item : source) { dest.add(item); }
    return dest;
}
0
20 мар. '12 в 23:31
источник

Здесь еще один подход, предположительно быстрый подход: http://javatechniques.com/blog/faster-deep-copies-of-java-objects/

0
03 апр. '09 в 21:51
источник

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

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

Пример

this.myObjectCloned = SerializationUtils.clone(this.object);
0
02 мая '13 в 19:25
источник

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

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

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

-1
23 нояб. '16 в 22:24
источник

Посмотрите другие вопросы по меткам или Задайте вопрос