Java ArrayList Deep Copy
In this article, we will look at how to perform deep copy of an ArrayList in java and also learn about shallow copy with example programs.
ArrayList clone()
method
java.util.ArrayList
has a clone()
method which creates its copy. The new ArrayList
contains all the elements of the original list as shown in the example below
ArrayList<Integer> list = new ArrayList(); list.add(1); list.add(2); list.add(3); ArrayList<Integer> clone = (ArrayList<Integer>)x.clone(); System.out.println("Original List: " + list); System.out.println("Clone List: " + clone);
Output of this code is
Original List: [1, 2, 3] Clone List: [1, 2, 3]
Both the lists are different. If any element of original list is modified, removed or a new element is added to this list, the cloned list remains unaffected.
But, this is applicable only when the data type of ArrayList elements is
1. primitives such as integers, floats etc.,
2. Objects of immutable classes such as String
or wrapper classes such as Integer
, Double
etc.
This is because when immutable objects are modified, a new object is created, thus, the value of original object remains unaffected.
When the original list is of objects(user defined or java objects), then even though the two lists are different, changes made to an object in the original list are also reflected in the cloned list.
This is because clone()
method of ArrayList
creates a Shallow copy.
Shallow copy means that only the copy of list is created. Elements of the original list are not copied into the clone list. Therefore, if any object is modified in the original list, it changes the cloned list as well
Below is an example program of shallow copy of ArrayList
.
Student.java
// public class Student { private String name; private int rollNo; public Student(String n, int r) { name = n; rollNo = r; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getRollNo() { return rollNo; } public void setRollNo(int rollNo) { this.rollNo = rollNo; } }
Code below creates an ArrayList
of Student
objects, clones it and modifies the name of first employee of original list.
// define list ArrayList<Student> students = new ArrayList<>(); // create objects Student s1 = new Student("A", 1); Student s2 = new Student("B", 2); // add to list students.add(s1); students.add(s2); // create a clone List<Student> clone = (ArrayList<Student>)students.clone(); System.out.println("Name of first student of clone: " + clone.get(0).getName()); System.out.println("Modifying name of student in source list"); // modify name of original list element students.get(0).setName("X"); System.out.println("Name of first student of clone: " + clone.get(0).getName());
Notice the output
Name of first student of clone: A
Modifying name of student in source list
Name of first student of clone: X
It changed the name of student in the clone list even though we changed only original list. This is what is meant by a shallow copy, which clone()
for ArrayList performs.
Javadoc of clone()
method also states,
Returns a shallow copy of this ArrayList instance. (The elements themselves are not copied.)
Deep copy of an
ArrayList
of objects means that even if objects of source list are modified, it should not affect the objects of cloned list.To perform deep copy, following steps need to be followed
1. Implement clone() method in the Employee
class or the class type of ArrayList
objects.
2. Instead of calling clone()
on ArrayList
, iterate over the list and add the cloned object to the new list.
Modified Student
class will be
public class Student implements Cloneable { private String name; private int rollNo; public Student(String n, int r) { name = n; rollNo = r; } // clone method protected Object clone() throws CloneNotSupportedException { return (Student)super.clone(); } public Student(String n, int r) { name = n; rollNo = r; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getRollNo() { return rollNo; } public void setRollNo(int rollNo) { this.rollNo = rollNo; } }
And code to create a deep copy of ArrayList
will be
// define list ArrayList<Student> students = new ArrayList<>(); // create objects Student s1 = new Student("A", 1); Student s2 = new Student("B", 2); // add to list students.add(s1); students.add(s2); // create a new list for clone List<Student> clone = new ArrayList<>(); // add elements to clone list students.forEach(s -> { try { clone.add((Student)s.clone()); } catch (CloneNotSupportedException e3) { e3.printStackTrace(); } }); System.out.println("Name of first student of clone: " + clone.get(0).getName()); System.out.println("Modifying name of student in source list"); // modify name of original list element students.get(0).setName("X"); System.out.println("Name of first student of clone: " + clone.get(0).getName());
Note that the clone list is a new ArrayList object and instead of calling clone()
on source list, we are iterating over the list and cloning each list element.
To iterate over the list you can use Java 8 forEach method or enhanced for loop.
Output of this code is
Name of first student of clone: A
Modifying name of student in source list
Name of first student of clone: A
Clone list remains unchanged.
It might happen that the
ArrayList
objects themselves contain other objects. In that case, we need to create a clone of the nested objects as well to perform actual deep cloning.Example, suppose the
Student
class also has a Date
field(which is mutable) as below.
public class Student implements Cloneable { private String name; private int salary; private Date dateOfBirth; public Employee(String n, int s, Date d) { name = n; salary = s; dateOfBirth = d; } // getter and setter methods }
So, if the value of this date is modified in an original list element, it is reflected in the cloned list as well.
To overcome this, we need to clone all the mutable fields in the clone()
method that we provide in our class. So, the modified clone()
method will be
// clone method protected Object clone() throws CloneNotSupportedException { Student s = (Student)super.clone(); // clone mutable object s.setDateOfBirth(dateOfBirth.clone()); return s; }
Now, even if we modify date field of original list, the corresponding value of cloned list will be unaffected. Example,
// define list ArrayList<Student> students = new ArrayList<>(); // create a date Date d = new Date(2000, 11, 31); // create objects Student s1 = new Student("A", 1, d); Student s2 = new Student("B", 2, d); // add to list students.add(s1); students.add(s2); // create a new list for clone List<Student> clone = new ArrayList<>(); // add elements to clone list students.forEach(s -> { try { clone.add((Student)s.clone()); } catch (CloneNotSupportedException e3) { e3.printStackTrace(); } }); // create a different date Date d1 = new Date(1999, 11, 31); System.out.println("Modifying date in source list"); // modify date of original list element students.get(0).setDateOfBirth(d1); System.out.println("Date of birth of original list: " + students.get(0).getDateOfBirth()); System.out.println("Date of birth of clone: " + clone.get(0).getDateOfBirth());
This prints
Date of birth of original list: Mon Dec 31 00:00:00 IST 3900
Date of birth of clone: Sun Dec 31 00:00:00 IST 3899
Look, both values are different.
Click the clap if the article was helpful.