zoukankan      html  css  js  c++  java
  • Java中如何克隆集合——ArrayList和HashSet深拷贝

      编程人员经常误用各个集合类提供的拷贝构造函数作为克隆ListSetArrayListHashSet或者其他集合实现的方法。需要记住的是,Java集合的拷贝构造函数只提供浅拷贝而不是深拷贝,这意味着存储在原始List和克隆List中的对象是相同的,指向Java堆内存中相同的位置。增加了这个误解的原因之一是对于不可变对象集合的浅克隆。由于不可变性,即使两个集合指向相同的对象是可以的。字符串池包含的字符串就是这种情况,更改一个不会影响到另一个。使用ArrayList的拷贝构造函数创建雇员List的拷贝时就会出现问题,Employee类不是不可变的。在这种情况下,如果原始集合修改了雇员信息,这个变化也将反映到克隆集合。同样如果克隆集合雇员信息发生变化,原始集合也会被更改。绝大多数情况下,这种变化不是我们所希望的,克隆对象应该与原始对象独立。解决这个问题的方法是深克隆集合,深克隆将递归克隆对象直到基本数据类型或者不可变类。本文将了解一下深拷贝ArrayList或者HashSet等集合类的一种方法。如果你了解深拷贝与浅拷贝之间的区别,那么理解集合深克隆的方法就会很简单。

    Java集合的深克隆

    下面例子有一个Employee集合,Employee是可变对象,成员变量namedesignation。它们存储在HashSet中。使用java.util.Collection接口的addAll()方法创建集合拷贝。然后修改存储在原始集合每个Employee对象的designation值。理想情况下这个改变不会影响克隆集合,因为克隆集合和原始集合应该相互独立,但是克隆集合也被改变了。修正这个问题的方法是对存储在Collection类中的元素深克隆。

     1 /**
     2  * 
     3  * @ClassName: CollectionCloningTest
     4  * TODO
     5  * @author xingle
     6  * @date 2015-3-20 下午3:32:22
     7  */
     8 public class CollectionCloningTest {
     9     
    10     public static void main(String[] args){
    11         ArrayList<Employee> org = new ArrayList<Employee>();
    12         org.add(new Employee("Joe", "Manager")); 
    13         org.add(new Employee("Tim", "Developer")); 
    14         org.add(new Employee("Frank", "Developer")); 
    15                
    16         Collection<Employee> copy = new HashSet<>(org);                
    17         
    18         System.out.println("原来的集合: "+org);
    19         System.out.println("复制的集合: "+copy);
    20         
    21         Iterator<Employee> orgItr = org.iterator();
    22         while(orgItr.hasNext()){ 
    23             orgItr.next().setDesignation("staff"); 
    24             
    25         }
    26 
    27         System.out.println("修改后原来的集合: "+org);
    28         System.out.println("修改后复制的集合: "+copy);
    29     }
    30 
    31 }
    32 
    33 
    34 class Employee { 
    35     private String name; 
    36     private String designation; 
    37      
    38     public Employee(String name, String designation) { 
    39         this.name = name; 
    40         this.designation = designation; 
    41     } 
    42      
    43     public String getDesignation() { 
    44         return designation; 
    45     } 
    46  
    47     public void setDesignation(String designation) { 
    48         this.designation = designation; 
    49     } 
    50  
    51     public String getName() { 
    52         return name; 
    53     } 
    54  
    55     public void setName(String name) { 
    56         this.name = name; 
    57     } 
    58  
    59     @Override
    60     public String toString() { 
    61         return String.format("%s: %s", name, designation ); 
    62     } 
    63 
    64 }

    执行结果:

    可以看到改变原始CollectionEmployee对象(改变designation为”staff“)在克隆集合中也有所反映,因为克隆是浅拷贝,指向堆中相同的Employee对象。为了修正这个问题,需要遍历集合,深克隆Employee对象,在这之前,要重写Employee对象的clone方法。

    1)Employee实现Cloneable接口
    2)为Employee类增加下面的clone()方法

    3)不使用拷贝构造函数,使用下面的代码来深拷贝集合

     1 public class CollectionCloningTest {
     2     
     3     public static void main(String[] args){
     4         ArrayList<Employee> org = new ArrayList<Employee>();
     5         org.add(new Employee("Joe", "Manager")); 
     6         org.add(new Employee("Tim", "Developer")); 
     7         org.add(new Employee("Frank", "Developer")); 
     8                
     9        //Collection<Employee> copy = new HashSet<>(org);
    10        Collection<Employee> copy = new HashSet<Employee>(org.size()); 
    11                 
    12         
    13         System.out.println("原来的集合: "+org);
    14         System.out.println("复制的集合: "+copy);
    15         
    16         Iterator<Employee> orgItr = org.iterator();
    17         while(orgItr.hasNext()){ 
    18             //orgItr.next().setDesignation("staff"); 
    19             copy.add(orgItr.next().clone());   
    20             
    21         }
    22         
    23 
    24         Iterator<Employee> orgItr2 = org.iterator();
    25         while(orgItr2.hasNext()){ 
    26             orgItr2.next().setDesignation("staff"); 
    27         } 
    28         System.out.println("修改后原来的集合: "+org);
    29         System.out.println("修改后复制的集合: "+copy);
    30     }
    31 
    32 }
    33 
    34 
    35 class Employee implements Cloneable{ 
    36     private String name; 
    37     private String designation; 
    38      
    39     public Employee(String name, String designation) { 
    40         this.name = name; 
    41         this.designation = designation; 
    42     } 
    43      
    44     public String getDesignation() { 
    45         return designation; 
    46     } 
    47  
    48     public void setDesignation(String designation) { 
    49         this.designation = designation; 
    50     } 
    51  
    52     public String getName() { 
    53         return name; 
    54     } 
    55  
    56     public void setName(String name) { 
    57         this.name = name; 
    58     } 
    59  
    60     @Override
    61     public String toString() { 
    62         return String.format("%s: %s", name, designation ); 
    63     } 
    64     
    65     @Override
    66     protected Employee clone(){
    67         try {
    68             Employee result = (Employee) super.clone();
    69             return result;
    70         } catch (CloneNotSupportedException e) {
    71              throw new RuntimeException(e); // won't happen 
    72         }
    73         
    74     }
    75 }

    执行结果:

    可以看到克隆集合和原始集合相互独立,它们指向不同的对象。

    这就是Java中如何克隆集合的内容。现在我们知道拷贝构造函数或者ListSet等各种集合类的addAll()方法仅仅创建了集合的浅拷贝,而且原始集合和克隆集合指向相同的对象。为避免这个问题,应该深克隆集合,遍历集合克隆每个元素。尽管这要求集合中的对象必须支持深克隆操作。

  • 相关阅读:
    pip不是内部或外部命令也不是可运行的程序或批处理文件的问题
    动态规划 leetcode 343,279,91 & 639. Decode Ways,62,63,198
    动态规划 70.climbing Stairs ,120,64
    (双指针+链表) leetcode 19. Remove Nth Node from End of List,61. Rotate List,143. Reorder List,234. Palindrome Linked List
    建立链表的虚拟头结点 203 Remove Linked List Element,82,147,148,237
    链表 206 Reverse Linked List, 92,86, 328, 2, 445
    (数组,哈希表) 219.Contains Duplicate(2),217 Contain Duplicate, 220(3)
    重装系统
    java常用IO
    端口
  • 原文地址:https://www.cnblogs.com/xingele0917/p/4354013.html
Copyright © 2011-2022 走看看