【轉】Java如何複製集合——深度拷貝ArrayList和HashSet

來源:互聯網
上載者:User

標籤:

原文網址:http://blog.csdn.net/cool_sti/article/details/21658521

原英文連結:http://javarevisited.blogspot.hk/2014/03/how-to-clone-collection-in-java-deep-copy-vs-shallow.html

 

程式員通常會誤用集合類(如List、Set、ArrayList、HashSet)所提供的拷貝建構函式或其它方法來完成集合的拷貝。值得記住的一點是,Java中集合提供的拷貝建構函式只支援淺拷貝而不是深拷貝,這意味著儲存在原有List和複製List中的對象會保持一致,並指向Java堆中同一記憶體位址。造成這一誤解的原因是它採用Collections對不可變對象進行了淺拷貝,正是由於對象不可變,兩個集合指向相同對象也是合乎情理的。正是如此,所以儲存在池中的String,更新其中一個不會影響到其它對象。問題來了,當我們使用ArrayList的拷貝建構函式來建立Employee對象列表的複製時,其中Employee對象是可變的,在這個例子中如果原有集合修改了其中一個Employee,改變同樣也會發生在拷貝的集合中,但這並不是我們所希望的。在幾乎所有的例子中,複製應該和原有對象獨立開來。能夠避免這個問題的方法是採用深度拷貝集合,即遞迴拷貝對象直到訪問到原語或不可變對象。在這篇文章中,我們將介紹一種深度拷貝集合的方法,如Java中的ArrayList或HashSet。順便提一下,如果你瞭解淺拷貝和深拷貝之間的區別,那麼你會很輕鬆地掌握深度拷貝集合的機理。Java集合中的深度拷貝在下面的例子中,我們有一個可變Employee對象的集合,每個對象包含name和designation欄位,將它們儲存在HashSet中。我們用java.util.Collection介面中的addAll()方法來建立這個集合的一個拷貝。在這之後,我們修改原有集合中的每個Employee對象的designation欄位,希望這個改變不會影響到拷貝集合,但事與願違,解決這個問題的方法是深度拷貝集合類中的元素。 [java] view plaincopy 
  1. import java.util.Collection;   
  2. import java.util.HashSet;   
  3. import java.util.Iterator;   
  4. import org.slf4j.Logger;   
  5. import org.slf4j.LoggerFactory;   
  6. /**  
  7.   * Java program to demonstrate copy constructor of Collection provides shallow  
  8.   * copy and techniques to deep clone Collection by iterating over them.  
  9.   * @author http://javarevisited.blogspot.com  
  10.   */   
  11. public class CollectionCloningTest {   
  12.     private static final Logger logger = LoggerFactory.getLogger(CollectionCloningclass);   
  13.       
  14.     public static void main(String args[]) {   
  15.       
  16.         // deep cloning Collection in Java   
  17.         Collection org = new HashSet();   
  18.         org.add(new Employee("Joe", "Manager"));   
  19.         org.add(new Employee("Tim", "Developer"));   
  20.         org.add(new Employee("Frank", "Developer"));   
  21.           
  22.         // creating copy of Collection using copy constructor   
  23.         Collection copy = new HashSet(org);   
  24.           
  25.         logger.debug("Original Collection {}", org);   
  26.         logger.debug("Copy of Collection {}", copy );   
  27.           
  28.         Iterator itr = org.iterator();   
  29.         while(itr.hasNext()){   
  30.             itr.next().setDesignation("staff");   
  31.         }   
  32.           
  33.         logger.debug("Original Collection after modification {}", org);   
  34.         logger.debug("Copy of Collection without modification {}", copy );   
  35.           
  36.         // deep Cloning List in Java   
  37.           
  38.     }   
  39. }  
  40. class Employee {   
  41.     private String name;   
  42.     private String designation;   
  43.       
  44.     public Employee(String name, String designation) {   
  45.         this.name = name; this.designation = designation;   
  46.     }   
  47.     public String getDesignation() {   
  48.         return designation;   
  49.     }   
  50.       
  51.     public void setDesignation(String designation) {   
  52.         this.designation = designation;   
  53.     }   
  54.       
  55.     public String getName() {   
  56.         return name;   
  57.     }   
  58.       
  59.     public void setName(String name) {   
  60.         this.name = name;   
  61.     }   
  62.       
  63.     @Override public String toString() {   
  64.         return String.format("%s: %s", name, designation );   
  65.     }   
  66. }  
輸出: [html] view plaincopy 
  1. - Original Collection [Joe: Manager, Frank: Developer, Tim: Developer]   
  2. - Copy of Collection [Joe: Manager, Frank: Developer, Tim: Developer]   
  3. - Original Collection after modification [Joe: staff, Frank: staff, Tim: staff]   
  4. - Copy of Collection without modification [Joe: staff, Frank: staff, Tim: staff]  
可以很清楚地看到修改原有集合的Employee對象(即修改designation欄位為"staff")也會反映到拷貝集合中,因為複製是淺拷貝,它指向的是堆中同一個Employee對象。為了修複這個問題,我們需要遍曆集合深度拷貝所有Employee對象,而在這之前,我們需要為Employee對象覆寫clone方法。1)讓Employee類實現Cloneable介面;2)在Employee類中添加下面的clone()方法; [java] view plaincopy 
  1. @Override   
  2. protected Employee clone() {   
  3.     Employee clone = null;   
  4.     try{   
  5.         clone = (Employee) super.clone();   
  6.           
  7.     }catch(CloneNotSupportedException e){   
  8.         throw new RuntimeException(e);  // won‘t happen   
  9.     }   
  10.       
  11.     return clone;   
  12. }  
3)使用下面的代碼,用Java中深度拷貝取代拷貝建構函式; [java] view plaincopy 
  1. Collection<Employee> copy = new HashSet<Employee>(org.size());   
  2. Iterator<Employee> iterator = org.iterator();   
  3. while(iterator.hasNext()){   
  4.     copy.add(iterator.next().clone());   
  5. }  
4)為修改後的集合運行同樣的代碼,會得到不同的輸出: [html] view plaincopy 
  1. - Original Collection after modification  [Joe: staff, Tim: staff, Frank: staff]  
  2. - Copy of Collection without modification [Frank: Developer, Joe: Manager, Tim: Developer]  
可以看出複製後的和原有集合相互獨立,分別指向不同的對象。這便是Java如何複製集合的所有內容,現在我們明白集合類中各種拷貝建構函式,如List或Set中addAll()方法,僅僅實現了集合的淺拷貝,即原有集合和拷貝的集合指向相同的對象。這也便要求在集合中儲存任何對象,必須支援深度拷貝操作。 PS: 原Blog中的代碼執行可能會遇到一些問題,但關鍵是這中間的思想很重要。 連結:How to create Immutable Class and Object in Java - Tutorial ExampleDifference between deep copy vs Shadow Cloning in JavaCollection interview  questions answersHow clone method works in Java

【轉】Java如何複製集合——深度拷貝ArrayList和HashSet

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.