Как сделать резервную копию ArrayList в Java?

У меня есть некоторые данные, хранящиеся как ArrayList. И когда я хочу сделать резервную копию этих данных, java навсегда ограничивает два объекта. Это означает, что когда я меняю значения в данных ArrayList, эти изменения попадают в резервную копию. Пробовал копировать значения из данных отдельно в резервную копию в цикле, пробовал использовать метод data.clone() - ничего не помогает.

Что вы имеете в виду под «резервным копированием»? Вы имеете в виду, что хотите создать еще один список ArrayList, содержащий то, что находится в текущем списке ArrayList?

Paul Tomblin 19.11.2008 22:52
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
10
1
27 854
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

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

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

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

Все эти процессы создают мелкие копии. Если вы изменяете свойства объектов в массиве, два массива имеют ссылки на один и тот же экземпляр.

List org = new java.util.ArrayList();
org.add(instance)
org.get(0).setValue("org val");
List copy = new java.util.ArrayList(org);
org.get(0).setValue("new val");

copy.get(0).getValue() также вернет "new val", потому что org.get(0) и copy.get(0) возвращают один и тот же экземпляр. Вы должны выполнить глубокую копию следующим образом:

List copy = new java.util.ArrayList();
for(Instance obj : org) {
    copy.add(new Instance(obj)); // call to copy constructor
}

Спасибо! У меня был тот же вопрос, что и у OP, и вы на него отлично ответили. DeepCopy против Shallow Copy! и, что более важно, как на самом деле сделать глубокую копию!

Mark Lakewood 22.12.2009 09:13

Ваш вопрос не очень ясен. Если вы clone () ArrayList, клон не будет изменен, если вы измените содержимое оригинала (то есть, если вы добавите или удалите элементы), но это «неглубокая копия», поэтому, если вы измените фактические объекты в оригинале, они будут также можно изменить в клоне.

Если вы хотите сделать «глубокую копию», чтобы изменения фактических объектов не повлияли на их резервные копии в клоне, вам необходимо создать новый список ArrayList, а затем просмотреть исходный и для каждого элемента клонировать это в новый. Как в

ArrayList backup = new ArrayList();
for (Object obj : data)
   backup.add(obj.clone());

Я предполагаю, что data - это имя ArrayList, для которого вы хотите сделать резервную копию. Если это так, вы должны знать, что clone не является глубокий - он только создает копию объекта, для которого он вызывается, который в данном случае является списком. Если бы это был глубокий клон, он бы заполнил новый список клонами объектов в нем.

Поскольку он неглубокий, если вы измените объекты, содержащиеся в списке, список резервных копий также отобразит эти изменения, поскольку он содержит те же объекты. Единственный случай, когда вы не увидите изменений в резервной копии после изменения «текущего» списка, - это когда вы добавляете или удаляете объекты из текущего списка.

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

Ответ принят как подходящий

Думаю, нужно .clone() по отдельным объектам. Клонирование ArrayList не «глубокое»; он только клонирует ссылки на объект.

Что касается проблемы клонирования, как только я решил это, сериализовав всю коллекцию в строку, а затем сериализовав ее обратно в новый объект. Это заставляет вас сделать все ваши объекты сериализуемыми и принимать, когда два объекта действительно хотят ссылаться на один третий объект, но это может быть довольно хорошим балансом простоты и полезности.

На самом деле, я не пробовал этого, но вы, вероятно, могли бы использовать канал для сериализации в одно и то же точное время, чтобы вы не хранили в памяти 3 копии (если это огромная коллекция)

Я еще не пробовал, но думаю, что Collections.copy это сделает.

[EDIT] Теперь я попробовал:

static String GetRandomString(int length)
{
  UUID uuid = UUID.randomUUID();
  return uuid.toString().substring(0, length);  
}

public static void main(String[] args)
{
  ArrayList<String> al = new ArrayList<String>(20);
  for (int i = 0; i < 10; i++)
  {
    al.add(GetRandomString(7));
  }
  ArrayList<String> cloneArray = new ArrayList<String>(al);
  Collections.copy(cloneArray, al);
  System.out.println(al);
  System.out.println(cloneArray);
  for (int i = 9; i >= 0; i -= 2)
  {
    al.remove(i);
  }
  System.out.println(al);
  System.out.println(cloneArray);
}

Я считаю, что на самом деле это не показывает глубокую копию, поскольку операция добавления / удаления не изменяет исходный список. Я попробовал ваш тот же код, но заменил содержимое списка настраиваемым классом и изменил содержимое (а не удалял его), затем скопированный список также был изменен.

Nrj 03.06.2010 08:54

@Nrj: ах, вы правы, списки независимы, но все же указывают (ссылаются) на одни и те же объекты, так что это все еще неглубокая копия. Мой плохой (эй, два года назад я не очень хорошо разбирался в Java! :-))

PhiLho 03.06.2010 17:02

Зависит от того, что вам нужно. Неглубокая копия (элементы в списке являются ссылками на те же, что и в оригинале):

ArrayList backup = new ArrayList(mylist.size());
backup.addAll(mylist);

Глубокая копия (элементы также являются копиями):

ArrayList backup = new ArrayList(mylist.size());
for(Object o : mylist) {
    backup.add(o.clone());
}

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

import java.util.ArrayList;

public class Snapshot {
    private ArrayList<Integer> dataBackup;

    public Snapshot(ArrayList<Integer> data)
    {
        dataBackup = new ArrayList<Integer>();
        for(int i = 0; i < data.size(); i++)
        {
            dataBackup.add(data.get(i));
        }
    }

    public ArrayList<Integer> restore()
    {
        return dataBackup;
    }

    public static void main(String[] args)
    {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);

        Snapshot snap = new Snapshot(list);

        list.set(0, 3);
        list = snap.restore();

        System.out.println(list); // Should output [1, 2]

        list.add(4);
        list = snap.restore();

        System.out.println(list); // Should output [1, 2]
    }
}

Другие вопросы по теме