1

I have a class, which contains a collection field (pets). This field contains other collections, which again contain objects. I want to create a deepcopy of one of this class' object.

I have read about using copy constructors, which seem to be way more comfortable than using the Cloneable interface. I implemented one in my Person class and made use of the ArrayList's copy constructor. Unfortuntely, the copy of the pets ArrayList was not a deepcopy - the contents still reference the same contents. Only the pets ArrayList itself got copied. So the constructor ArrayList(Collection c) is not doing what I want it to do. But I have also read about iterating through collections to copy its contents again - this is what I did in the example below. Up to here the pets ArrayList and the contents, the ArrayLists containing different animals, are cloned.

But what about the animal objects in the animal list? John and JohnClone have their own pet lists, but the pets are still the same. If something happens to John's dog Odin, JohnClone's dog will still be affected.

What am I missing out? How do I create a real deepcopy of a collection?

public class Person {

String name;
int age;
ArrayList pets;

public Person(String name, int age, ArrayList pets) {
    this.name = name;
    this.age = age;
    this.pets = pets;
}

public Person(Person person) {
    name = person.name;
    age = person.age;

    // pets = ArrayList(person.pets) didn't copy its contents
    ArrayList clone = new ArrayList();
    for (Object list : person.pets) { 
        clone.add(new ArrayList((ArrayList) list));
    }
    pets = clone;
}

public static void main(String[] args) throws Exception {

    ArrayList dogs = new ArrayList();
    dogs.add(new Dog("Odin"));
    dogs.add(new Dog("Hachiko"));

    ArrayList cats = new ArrayList();
    cats.add(new Cat("Whisky"));

    ArrayList johnsPets = new ArrayList();
    johnsPets.add(dogs);
    johnsPets.add(cats);

    Person john = new Person("John Doe", 33, johnsPets);
    Person johnClone = new Person(john);
}

I left Person's fields default and didn't use generics in the collections to not unneccesary bloat this short example.

DeMo
  • 683
  • 5
  • 14

2 Answers2

1

I guess I would stick to a solution similar to the one Suresh Sajja mentioned for cloning objects that are not too deeply nested.

Otherwise I read about serializing objects to create a deep copy: Deep cloning objects. I guess these are the solutions one has to use due to the fact that Java provides no such method.

Community
  • 1
  • 1
DeMo
  • 683
  • 5
  • 14
0

Do the same way as you are cloning animal lists in pets. Construct the array list with clones of Objects

ArrayList clone = new ArrayList();
for (Object list : person.pets) {
    ArrayList objClone = new ArrayList();
     for(Object obj : list){
      objClone.add(obj.clone()); //you have to honour the contract of cloneable while doing this on dog, cat objects
    }
    clone.add(objclone);
}
pets = clone;
Suresh Sajja
  • 552
  • 3
  • 8
  • I tried your solution but I had to modify it a bit. At first I want to avoid using Cloneable, so I added a copy constructor to Dog and Cat. I had to use instanceof to decide, which animal I have to create. All in all the snippet works like it should. But this solution requires that I know exactly, how deep my collection is nested. Additionally, I would have to use many instanceof's when more animals than dogs and cats are possible. Back to Cloneable: is there a reason you would want to use obj.clone() instead of providing a copy constructor for dogs and cats? – DeMo Oct 23 '14 at 09:36
  • To avoid instanceof check, you have to start using generics – Suresh Sajja Oct 23 '14 at 09:41