Question

I have some data stored as ArrayList. And when I want to backup this data,java bounds two objects forever. Which means when I change values in data ArrayList this changes come to backup. I tried to copy values from data separately to backup in the loop, tried to use method data.clone() — nothing helps.

Was it helpful?

Solution

I think you need to .clone() the individual objects. Cloning the ArrayList is not "deep"; it will only clone the references to the object.

OTHER TIPS

Your question isn't very clear. If you clone() an ArrayList, the clone will not be modified if you change the contents of the original (ie if you add or remove elements) but it's a "shallow copy" so if you change the actual objects in the original they will also be changed in the clone.

If you want to make a "deep copy", so that changes to the actual objects will not affect the backups of them in the clone, then you need to create a new ArrayList and then go through the original one and for each element, clone it into the new one. As in

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

I'm assuming data is the name of the ArrayList you wanted to backup. If so, you should know that clone is not deep - it only creates a copy of the object on which it is invoked, which in this case is the list. If it were a deep clone, it would fill the new list with clones of the objects in it.

Since it's not deep, if you change the objects the list contains, then the backup list will show those changes as well, since it's containing the same objects. The only time you'll not see changes in the backup after changing the "current" list is when you add or remove objects from the current list.

Some classes may override clone to be deep, but not all. In general, it's not something you can rely on. When creating a backup copy of Java collections, remember to either clone the contained objects as well, or deal only with collections of immutable objects.

All of these processes make shallow copies. If you're changing properties of objects with in the array, the two arrays have references to the same instance.

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() will return "new val" as well because org.get(0) and copy.get(0) return the exact same instance. You have to perform a deep copy like so:

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

Depends what you need. Shallow copy (items in list are references to the same as in the original):

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

Deep copy (items are copies also):

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

It sounds (if I interpret your question properly; it's a little tough) like you're not copying the data that you're referring to in your ArrayList to your backup; you're copying the reference.

It's hard to say exactly how to solve your problem without knowing what your data type is that you're storing / backing up, but just be sure that you're copying the elements of the data contained within the ArrayList. That would mean, among other things, to do things like perform a clone() on the elements of the list, but not on the ArrayList (because that'll create a new cloned list with copies of the references to the same objects).

Regarding the cloning problem, once I solved this by serializing the entire collection into a string then serializing it back out into a new object. This forces you to make all your objects serializable and take when two objects really want to reference a single third object, but it can be a pretty good balance of simplicity and usefulness.

Actually, I haven't tried this but you could probably use a pipe to serialize in and out at the same exact time so that you aren't storing 3 copies in memory (if it's a huge collection)

Here is a fully functioning ArrayList backup class that checks if the ArrayList already exists. Basically it's just a for-loop cycling through the list and manually adding them to the new list.

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]
    }
}

I haven't tried it yet, but I think Collections.copy will do that.

[EDIT] Now, I tried:

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);
}

You could write an object that wraps two ArrayLists. Anything write it so that it adds, removes, and modifies data in both at the same time.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top