Search code examples
javacopyclonecopy-constructordeep-copy

Clone collection containing another collection


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.


Solution

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