Sunday, August 7, 2011

Difference between Shallow Copy and Deep Copy

Shallow Copy
This is a result of the default cloning functionality provided by the Object.clone() method if the class has non-primitive data type members as well. Shallow Copy concept is not applicable to the classes having only primitive data type members as in that case the default cloning will also result into a Deep Copy only.

In case of Shallow Copy, the cloned object also refers to the same object to which the original object refers as only the object references gets copied and not the referred objects themselves. That's why the name Shallow Copy. Read more about Cloning More Details
sample program :
package com.sallowcopy;
/**
*
* @author thanooj
*
*/
class Person implements Cloneable {
// Lower-level object
private Car car;
private String name;
private Integer no;

public Car getCar() {
return car;
}
public String getName() {
return name;
}
public void setName(String s) {
name = s;
}
public Person(Integer n, String s, String t) {
no = n;
name = s;
car = new Car(t);
}
public Object clone() {
// shallow copy
try {
return super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
public void setNo(Integer no) {
this.no = no;
}
public Integer getNo() {
return no;
}
}

class Car {

private String name;

public String getName() {
return name;
}
public void setName(String s) {
name = s;
}
public Car(String s) {
name = s;
}
}

public class ShallowCopyTest {

public static void main(String[] args) {
// Original Object
Person p = new Person(new Integer(1000), "sreeRama", "Garuda");
System.out.println("Original (orginal values): " + p.getNo() + " "
+ p.getName() + " - " + p.getCar().getName());
// Clone as a shallow copy
Person q = (Person) p.clone();
System.out.println("Clone (before change): " + q.getNo() + " "
+ q.getName() + " - " + q.getCar().getName());
// change the primitive member
q.setName("Indra");
q.setNo(new Integer(2000));
// change the lower-level object
q.getCar().setName("Iravath");
System.out.println("Clone (after change): " + q.getNo() + " "
+ q.getName() + " - " + q.getCar().getName());
System.out.println("Original (after clone is modified): " + p.getNo()
+ " " + p.getName() + " - " + p.getCar().getName());
}
}
//output :
Original (orginal values): 1000 sreeRama - Garuda
Clone (before change): 1000 sreeRama - Garuda
Clone (after change): 2000 Indra - Iravath
Original (after clone is modified): 1000 sreeRama - Iravath
-------------------------------------------
Deep Copy

We need to override the clone() method for the classes having non-primitive type members to achieve Deep Copy as Deep Copy requires the member objects to be cloned as well, which is not done by the default cloning mechanism. Why is it not done by the default cloning? Because clone() is a method of the Object class and at that level it's not known what a typical class can have as its members and hence only a field-by-field copy approach has been provided as the default cloning mechanism.

Implementing Deep Copy in Java

For Deep Copy, we need to ensure that the member classes also implement the Cloneable interface otherwise calling the clone() method on the objects of those classes will result into CloneNotSupportedException. So, to implement Deep Copy, we first need to ensure that all the member classes (at all the levels - like if the member class itself has a member of some class type then that class as well... and so on) are implementing the Cloneable interface. After that we override the clone() method in all those classes (even in the classes where we have only primitive type members otherwise we would not be able to call the protected clone()method of Object class on the instances of those classes inside some other class ... a typical restriction of the protected access. We'll cover this in a separate article) and finally calling clone() method on the object members in the overriden clone() method definition.
sample program :

package com.deepcopy;
/**
*
* @author thanooj
*
*/
class Person implements Cloneable {
// Lower-level object
private Car car;
private String name;

public Car getCar() {
return car;
}
public String getName() {
return name;
}
public void setName(String s) {
name = s;
}
public Person(String s, String t) {
name = s;
car = new Car(t);
}
public Object clone() throws CloneNotSupportedException{
// Deep copy
Person p = (Person) super.clone();
p.car = (Car) p.car.clone();
return p;
}
}
class Car implements Cloneable{

private String name;

public String getName() {
return name;
}
public void setName(String s) {
name = s;
}
public Car(String s) {
name = s;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class DeepCopyTest {

public static void main(String[] args) {
// Original Object - before Deep cloning
Person p = new Person("sreeRama", "Garuda");
System.out.println(p.getName()+" "+p.getCar().getName());
Person pClone = null;
try {
pClone = (Person) p.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println("Clone (before change): "+pClone.getName()+" "+pClone.getCar().getName());
pClone.setName("Indra");
pClone.getCar().setName("Iravath");
System.out.println("Clone (after change): "+pClone.getName()+" "+pClone.getCar().getName());
// Original Object - after Deep cloning
System.out.println(p.getName()+" "+p.getCar().getName());
}
}
// Output :
sreeRama Garuda
Clone (before change): sreeRama Garuda
Clone (after change): Indra Iravath
sreeRama Garuda