Java中如何克隆集合——ArrayList和HashSet深拷贝
2019獨角獸企業重金招聘Python工程師標準>>>
編程人員經常誤用各個集合類提供的拷貝構造函數作為克隆List,Set,ArrayList,HashSet或者其他集合實現的方法。需要記住的是,Java集合的拷貝構造函數只提供淺拷貝而不是深拷貝,這意味著存儲在原始List和克隆List中的對象是相同的,指向Java堆內存中相同的位置。增加了這個誤解的原因之一是對于不可變對象集合的淺克隆。由于不可變性,即使兩個集合指向相同的對象是可以的。字符串池包含的字符串就是這種情況,更改一個不會影響到另一個。使用ArrayList的拷貝構造函數創建雇員List的拷貝時就會出現問題,Employee類不是不可變的。在這種情況下,如果原始集合修改了雇員信息,這個變化也將反映到克隆集合。同樣如果克隆集合雇員信息發生變化,原始集合也會被更改。絕大多數情況下,這種變化不是我們所希望的,克隆對象應該與原始對象獨立。解決這個問題的方法是深克隆集合,深克隆將遞歸克隆對象直到基本數據類型或者不可變類。本文將了解一下深拷貝ArrayList或者HashSet等集合類的一種方法。如果你了解深拷貝與淺拷貝之間的區別,那么理解集合深克隆的方法就會很簡單。
Java集合的深克隆
下面例子有一個Employee集合,Employee是可變對象,成員變量name和designation。它們存儲在HashSet中。使用java.util.Collection接口的addAll()方法創建集合拷貝。然后修改存儲在原始集合每個Employee對象的designation值。理想情況下這個改變不會影響克隆集合,因為克隆集合和原始集合應該相互獨立,但是克隆集合也被改變了。修正這個問題的方法是對存儲在Collection類中的元素深克隆。
package?javaBasic;import?java.util.Collection; import?java.util.HashSet; import?java.util.Iterator;/***?Java?program?to?demonstrate?copy?constructor?of?Collection?provides?shallow*?copy?and?techniques?to?deep?clone?Collection?by?iterating?over?them.*?*?@author?http://javarevisited.blogspot.com*/ public?class?CollectionCloningTest?{public?static?void?main(String?args[])?{//?deep?cloning?Collection?in?JavaCollection<Employee>?org?=?new?HashSet<Employee>();org.add(new?Employee("Joe",?"Manager"));org.add(new?Employee("Tim",?"Developer"));org.add(new?Employee("Frank",?"Developer"));//?creating?copy?of?Collection?using?copy?constructorCollection<Employee>?copy?=?new?HashSet<Employee>(org);System.out.println("Original?Collection?{}?"?+?org);System.out.println("Copy?of?Collection?{}?"?+?copy);Iterator<Employee>?itr?=?org.iterator();while?(itr.hasNext())?{itr.next().setDesignation("staff");}System.out.println("Original?Collection?after?modification?{}?"?+?org);System.out.println("Copy?of?Collection?without?modification?{}?"?+?copy);//?deep?Cloning?List?in?Java} }class?Employee?{private?String?name;private?String?designation;public?Employee(String?name,?String?designation)?{this.name?=?name;this.designation?=?designation;}public?String?getDesignation()?{return?designation;}public?void?setDesignation(String?designation)?{this.designation?=?designation;}public?String?getName()?{return?name;}public?void?setName(String?name)?{this.name?=?name;}@Overridepublic?String?toString()?{return?String.format("%s:?%s",?name,?designation);} }輸出:
| 1 2 3 4 | - Original Collection [Joe: Manager, Frank: Developer, Tim: Developer] - Copy of Collection [Joe: Manager, Frank: Developer, Tim: Developer] - Original Collection after modification [Joe: staff, Frank: staff, Tim: staff] - Copy of Collection without modification [Joe: staff, Frank: staff, Tim: staff] |
可以看到改變原始Collection中Employee對象(改變designation為”staff“)在克隆集合中也有所反映,因為克隆是淺拷貝,指向堆中相同的Employee對象。為了修正這個問題,需要遍歷集合,深克隆Employee對象,在這之前,要重寫Employee對象的clone方法。
1)Employee實現Cloneable接口
2)為Employee類增加下面的clone()方法
| 1 2 3 4 5 6 7 8 9 10 11 12 | @Override ???? protected Employee clone() { ???????? Employee clone = null ; ???????? try { ???????????? clone = (Employee) super .clone(); ???????? } catch (CloneNotSupportedException e){ ???????????? throw new RuntimeException(e); // won't happen ???????? } ?????????? ???????? return clone; ???? } |
3)不使用拷貝構造函數,使用下面的代碼來深拷貝集合
| 1 2 3 4 5 6 | Collection<Employee> copy = new HashSet<Employee>(org.size()); Iterator<Employee> iterator = org.iterator(); while (iterator.hasNext()){ ???? copy.add(iterator.next().clone()); } |
Code
package?javaBasic;import?java.util.Collection; import?java.util.HashSet; import?java.util.Iterator;/***?Java?program?to?demonstrate?copy?constructor?of?Collection?provides?shallow*?copy?and?techniques?to?deep?clone?Collection?by?iterating?over?them.*?*?@author?http://javarevisited.blogspot.com*/ public?class?CollectionCloningTest?{public?static?void?main(String?args[])?{//?deep?cloning?Collection?in?JavaCollection<Employee>?org?=?new?HashSet<Employee>();org.add(new?Employee("Joe",?"Manager"));org.add(new?Employee("Tim",?"Developer"));org.add(new?Employee("Frank",?"Developer"));//?creating?copy?of?Collection?using?copy?constructor//?Collection<Employee>?copy?=?new?HashSet<Employee>(org);/***?不使用拷貝構造函數,使用下面的代碼來深拷貝集合*/Collection<Employee>?copy?=?new?HashSet<Employee>(org.size());Iterator<Employee>?iterator?=?org.iterator();while?(iterator.hasNext())?{copy.add(iterator.next().clone());}System.out.println("Original?Collection?{}?"?+?org);System.out.println("Copy?of?Collection?{}?"?+?copy);Iterator<Employee>?itr?=?org.iterator();while?(itr.hasNext())?{itr.next().setDesignation("staff");}System.out.println("Original?Collection?after?modification?{}?"?+?org);System.out.println("Copy?of?Collection?without?modification?{}?"?+?copy);//?deep?Cloning?List?in?Java} }class?Employee?implements?Cloneable?{private?String?name;private?String?designation;public?Employee(String?name,?String?designation)?{this.name?=?name;this.designation?=?designation;}public?String?getDesignation()?{return?designation;}public?void?setDesignation(String?designation)?{this.designation?=?designation;}public?String?getName()?{return?name;}public?void?setName(String?name)?{this.name?=?name;}@Overridepublic?String?toString()?{return?String.format("%s:?%s",?name,?designation);}@Overrideprotected?Employee?clone()?{Employee?clone?=?null;try?{clone?=?(Employee)?super.clone();}?catch?(CloneNotSupportedException?e)?{throw?new?RuntimeException(e);?//?won't?happen}return?clone;} }4)運行相同的代碼更改原始集合,克隆集合不會也被更改。
| 1 2 | - Original Collection after modification? [Joe: staff, Tim: staff, Frank: staff] - Copy of Collection without modification [Frank: Developer, Joe: Manager, Tim: Developer] |
可以看到克隆集合和原始集合相互獨立,它們指向不同的對象。
這就是Java中如何克隆集合的內容。現在我們知道拷貝構造函數或者List或Set等各種集合類的addAll()方法僅僅創建了集合的淺拷貝,而且原始集合和克隆集合指向相同的對象。為避免這個問題,應該深克隆集合,遍歷集合克隆每個元素。盡管這要求集合中的對象必須支持深克隆操作。
轉載于:https://my.oschina.net/u/1412027/blog/223854
總結
以上是生活随笔為你收集整理的Java中如何克隆集合——ArrayList和HashSet深拷贝的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到好多人打架是怎么回事
- 下一篇: Java笔试之Singleton