Deep Clone Collection Objects in Java

Deep cloning of object has always been something that every Java developers work on so frequently. There are a lot of articles talking about a different way to clone objects deeply and obviously, the preferred method is to use Copy Constructors since it overcomes the design issues of Object.clone() and provides better control over object construction.

Clone an Object

For example, if I want to have a deep copy of Dog object, then I would use constructor as:

class Dog {
    private String name;
    private int age;

    public Dog(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    //copy constructor to create the copy of the Dog object
    public Dog(Dog dog) {
        this.name = dog.name;
        this.age = dog.age;
    }
    // getters and setters
}

Some of the notable advantages of using them over Objects.clone() are:

  • This doesn't force us to implement any interface or throw any exception but we can surely do it if it is required.
  • This allows us to have complete control over object creation, we can write our initialization logic in it.

 

Clone a Collection

With new feature Streams introduced in Java 8, it has been the easiest ever to clone the collections because when a stream is created, the instance will not modify its source. This allows the creation of multiple instances from a single source.     

For example, if the List of Dogs can be cloned as below:

Dog dog1 = new Dog("Puppy", 4);
Dog dog2 = new Dog("Tom", 5);
Dog dog3 = new Dog("Hen", 3);
Dog dog4 = new Dog("Jen", 7);
List<Dog> dogs = new ArrayList<>();
dogs.add(dog1);
dogs.add(dog2);
dogs.add(dog3);
dogs.add(dog4);

//clone with java 8
List<Dog> clonedList = dogs.stream().map(Dog::new).collect(Collectors.toList());

Now, whatever modification you make to original collection doesn't affect the cloned collection and vice-versa. 

//clone with java 8
List<Dog> clonedList = dogs.stream().map(Dog::new).collect(Collectors.toList());

assertThat(dogs, is(clonedList));

// modify original list
dogs.remove(0);
// make sure cloned list remains unaffected
assertEquals(3, dogs.size());
assertEquals(4, clonedList.size());

// modify cloned list
clonedList.add(new Dog("New", 3));
// make sure original list remains unaffected
assertEquals(3, dogs.size());
assertEquals(5, clonedList.size());

// modify element in dogs
dog2.setName("Very New Name");

assertEquals("Very New Name", dogs.get(0).getName());
assertEquals("Tom", clonedList.get(1).getName());

 

The source code for the example presented above is available on GitHub.

Java Stream Collectors
Regex to match the IP Address