Programmers often misuse the copy constructors provided by each collection class as clones, List
Set
ArrayList
HashSet
or methods implemented by other collections. It should be remembered that the copy constructor for the Java collection only provides a shallow copy rather than a deep copy, which means that the objects stored in the original list and the clone list are the same, pointing to the same location in the Java heap memory. One of the reasons for this misunderstanding is the shallow cloning of a set of immutable objects. Because of immutability, it is possible even if two sets point to the same object. The string pool contains a string that is the case, and changing one does not affect the other. ArrayList
A problem occurs when you use a copy constructor to create a copy of an employee list, and the Employee
class is not immutable. In this case, if the original collection modifies the employee information, the change is also reflected in the Clone collection. Similarly, if the Clone collection employee information changes, the original collection is also changed. In most cases, this change is not what we want, and the cloned object should be independent of the original object. The solution to this problem is to deeply clone the collection , and the deep clone will recursively clone the object to the base data type or to the immutable class. This article will look ArrayList
HashSet
at a method of deep copy or a collection class. If you understand the difference between a deep copy and a shallow copy, then it is easy to understand how a set of deep clones is understood.
Deep cloning of Java collections
The following example has a Employee
collection where employee is a mutable object, a member variable, name
and a designation
. They are stored in HashSet
. java.util.Collection
Create a copy of the collection using the method of the interface addAll()
. Then modify the value of each object stored in the original collection Employee
designation
. Ideally this change will not affect the Clone collection, because the Clone collection and the original collection should be independent of each other, but the clone collection is also changed. The way to fix this problem is to Collection
deeply clone the elements stored in the class.
1 /**2 * 3 * @ClassName: Collectioncloningtest4 * TODO5 * @authorXingle6 * @date 2015-3-20 pm 3:32:227 */8 Public classCollectioncloningtest {9 Ten Public Static voidMain (string[] args) { Onearraylist<employee> org =NewArraylist<employee>(); AOrg.add (NewEmployee ("Joe", "Manager")); -Org.add (NewEmployee ("Tim", "Developer")); -Org.add (NewEmployee ("Frank", "Developer")); the -collection<employee> copy =NewHashset<>(org); - -System.out.println ("original collection:" +org); +System.out.println ("copied collection:" +copy); - +Iterator<employee> Orgitr =org.iterator (); A while(Orgitr.hasnext ()) { atOrgitr.next (). Setdesignation ("Staff")); - - } - -System.out.println ("Modified original collection:" +org); -System.out.println ("copied collection after modification:" +copy); in } - to } + - the classEmployee{ * PrivateString name; $ PrivateString designation;Panax Notoginseng - PublicEmployee (string name, string designation) { the This. Name =name; + This. designation =designation; A } the + PublicString getdesignation () { - returndesignation; $ } $ - Public voidsetdesignation (String designation) { - This. designation =designation; the } - Wuyi PublicString GetName () { the returnname; - } Wu - Public voidsetName (String name) { About This. Name =name; $ } - - @Override - PublicString toString () { A returnString.Format ("%s:%s", name, designation); + } the -}
Execution Result:
You can see that changing the original Collection
Employee
object (changing designation to " staff
") is also reflected in the Clone collection, because the clone is a shallow copy, pointing to the same object in the heap Employee
. To fix this problem, you need to iterate through the collection, deep-clone the Employee
object, and before that, rewrite the Employee
object's Clone method.
1) Employee
Implement Cloneable
interface
2) Employee
Add the following method to the class clone()
3) Do not use the copy constructor, use the following code to deep copy the collection
1 Public classCollectioncloningtest {2 3 Public Static voidMain (string[] args) {4arraylist<employee> org =NewArraylist<employee>();5Org.add (NewEmployee ("Joe", "Manager")); 6Org.add (NewEmployee ("Tim", "Developer")); 7Org.add (NewEmployee ("Frank", "Developer")); 8 9 //collection<employee> copy = new hashset<> (org);Tencollection<employee> copy =NewHashset<employee>(Org.size ()); One A -System.out.println ("original collection:" +org); -System.out.println ("copied collection:" +copy); the -Iterator<employee> Orgitr =org.iterator (); - while(Orgitr.hasnext ()) { - //Orgitr.next (). Setdesignation ("Staff"); + Copy.add (Orgitr.next (). Clone ()); - + } A at -Iterator<employee> ORGITR2 =org.iterator (); - while(Orgitr2.hasnext ()) { -Orgitr2.next (). Setdesignation ("Staff")); - } -System.out.println ("Modified original collection:" +org); inSystem.out.println ("copied collection after modification:" +copy); - } to + } - the * classEmployeeImplementscloneable{ $ PrivateString name;Panax Notoginseng PrivateString designation; - the PublicEmployee (string name, string designation) { + This. Name =name; A This. designation =designation; the } + - PublicString getdesignation () { $ returndesignation; $ } - - Public voidsetdesignation (String designation) { the This. designation =designation; - } Wuyi the PublicString GetName () { - returnname; Wu } - About Public voidsetName (String name) { $ This. Name =name; - } - - @Override A PublicString toString () { + returnString.Format ("%s:%s", name, designation); the } - $ @Override the protectedEmployee Clone () { the Try { theEmployee result = (employee)Super. Clone (); the returnresult; -}Catch(clonenotsupportedexception e) { in Throw NewRuntimeException (e);//won ' t happen the } the About } the}
Execution Result:
You can see that the Clone collection and the original collection are independent of each other, pointing to different objects.
This is how the contents of a collection are cloned in Java . Now we know that a copy constructor List
or Set
a method of a collection class addAll()
only creates a shallow copy of the collection, and the original and cloned collections point to the same object. To avoid this problem, you should clone the collection deeply and iterate through the collection to clone each element. Although this requires that the objects in the collection must support deep cloning operations.
How to Clone collections--arraylist and HashSet deep copy in Java