Wednesday, February 13, 2013

Shallow Copy vs Deep Copy in Java

Copy/Clone mechanism of Java is used to return a new copy of the given object. The copied object will be a new instance, but the exact meaning of copy may depend on several factors.
(Revisit Java clone API : Read about clone basics)

Instead of heading straight to the definition, let's start with an example:

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

 public class CloneTest implements Cloneable {  
      private Person p;  
      private int a;  
   
      public CloneTest(Person p, int a) {  
           super();  
           this.p = p;  
           this.a = a;  
      }  
   
      public void printData() {  
           System.out.format("\n a = %d; Person : name=%s & age=%d", a, p.name,  
                     p.age);  
      }  
   
      public Object clone() throws CloneNotSupportedException {  
           return super.clone();  
      }  
   
      public static void main(String[] arg) throws CloneNotSupportedException {  
           CloneTest original = new CloneTest(new Person("Sid", 30), 1);  
           CloneTest clone1 = (CloneTest) original.clone();  
   
           // Data after cloning  
           original.printData();  
           clone1.printData();  
   
           clone1.p.age = 60; // modify contained object  
           clone1.a = 2; // modify the attribute  
   
           // Data after changes to cloned object  
           original.printData();  
           clone1.printData();  
      }  
 }  
   

Output
 a = 1; Person : name=Sid & age=30  //original Object
 a = 1; Person : name=Sid & age=30  //cloned Object
 a = 1; Person : name=Sid & age=60  //original after changes
 a = 2; Person : name=Sid & age=60  //cloned object after changes


Points To be Noted :
  1. Changes to the cloned Person object is getting reflected in the original object as well.
  2. Changes to the direct attribute(field i.e. a) of cloned object is not updated in original object.
     Let's see the same through a diagram :


 So what kind of copy is created by cloning as shown above ??

Shallow Copy 

Shallow copy is a bit-wise copy of an object. New object has an exact copy of the values in the original object. If any of the fields of the object are references to other object then only the reference addresses are copied (and not the content). 

In above example: Original as well as Clone1 are pointing to the same Person object, but there is a unique copy for the field, a.

Important point to be noted from the Person class :
Person class doesn't implement Cloneable interface. Is that reason for cloned and original object pointing to the same Person object? 
This can be verified easily with a small tweak:

 public class Person implements Cloneable {  
      public String name;  
      public int age;  
   
      public Person(String name, int age) {  
           this.name = name;  
           this.age = age;  
      }  
   
      public Object clone() throws CloneNotSupportedException {  
           // Person p= new Person(name, age);  
           System.out.println(" This method doesn't get called even");  
           return super.clone();  
      }  
 }  

Now run CloneTest class again and notice the output.
Output is still the same and in fact Person class' clone() method is not called at all (Print statement put in its clone method is not getting printed.)

This confirms that, copying through default clone mechanism is a SOFT COPY

 

Deep Copy

So now it's easy to understand what is a Deep Copy :


A Deep Copy, copies all fields, and makes copies of dynamically allocated memory pointed to by fields. Deep copy occurs when an object is copied along with the objects to which it refers.
 
In our case, deep copy will have a separate Person object so that changes in Clone1 doesn't get reflected in Original object.

Can't we achieve it for our CloneTest class ??
Yes, it can be done with a small tweak.


 public class Person implements Cloneable {  
      public String name;  
      public int age;  
   
      public Person(String name, int age) {  
           this.name = name;  
           this.age = age;  
      }  
   
      public Object clone() throws CloneNotSupportedException {  
           return super.clone();  
      }  
 }  

 public class CloneTest implements Cloneable {  
      private Person p;  
      private int a;  
   
      public CloneTest(Person p, int a) {  
           super();  
           this.p = p;  
           this.a = a;  
      }  
   
      public void printData() {  
           System.out.format("\n a = %d; Person : name=%s & age=%d", a, p.name,  
                     p.age);  
      }  
   
      public Object clone() throws CloneNotSupportedException {  
           CloneTest ct = (CloneTest) super.clone();  
           ct.p = (Person) p.clone();  
           return (Object) ct;  
      }  
   
      public static void main(String[] args) throws CloneNotSupportedException {  
           CloneTest original = new CloneTest(new Person("Sid", 30), 1);  
           CloneTest clone1 = (CloneTest) original.clone();  
   
           // Data after cloning  
           original.printData();  
           clone1.printData();  
   
           clone1.p.age = 60; // modify contained object  
           clone1.a = 2; // modify the attribute  
   
           // Data after changes to cloned object  
           original.printData();  
           clone1.printData();  
      }  
 } 

And Output is :
 a = 1; Person : name=Sid & age=30  //original Object
 a = 1; Person : name=Sid & age=30  //cloned Object
 a = 1; Person : name=Sid & age=30  //original after changes
 a = 2; Person : name=Sid & age=60  //cloned object after changes


So now the modified clone() method supports deep copy. Please note that clone method explicitly clones Person object.

Alternative Techniques

Below are some of the alternatives of creating clone of objects. 

Copy Constructor or copy factory 
Understanding and successfully implementing Cloneable interface is tricky and risk prone. So you can 
use constructors/factory method to create copy of the given object.

     public CloneTest createCloneTest(){  
         Person pt = new Person(p.name, p.age);  
         CloneTest ct = new CloneTest(pt,a);  
         return ct;  
       } 

So you just need to call this method to clone an object of class CloneTest.

Deep copy through Serialization
Deep copy means that your are duplicating the entire web of objects, rather than just the basic object
and its references. Serialization can be used to achieve deep copy.
When you deep copy through Serialization, make sure that all classes in object's graph are Serializable.

 public class Person implements Serializable{ }

 import java.io.ByteArrayInputStream;  
 import java.io.ByteArrayOutputStream;  
 import java.io.IOException;  
 import java.io.ObjectInputStream;  
 import java.io.ObjectOutputStream;  
 import java.io.Serializable;  
   
 public class CloneTest implements Serializable {  
   
      public CloneTest deepCopy(CloneTest t) throws IOException,  
                ClassNotFoundException {  
           ByteArrayOutputStream buf = new ByteArrayOutputStream();  
           ObjectOutputStream o = new ObjectOutputStream(buf);  
           o.writeObject(t);  
   
           ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(  
                     buf.toByteArray()));  
           return (CloneTest) in.readObject();  
      }  
      // no change in other methods except you need to call this method to clone  
 }            

Please note that, buffer is used in deepCopy method instead of file to serialize object. For ObjectOutputStream it's all the  same.

---
keep coding !!!

2 comments:

  1. very good tutorial keep it up

    ReplyDelete
  2. Thank u...nice tutorial

    ReplyDelete