Java如何克隆集合——深度拷贝ArrayList和HashSet
2014-03-21 00:05
651 查看
原英文链接: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。顺便提一下,如果你了解浅拷贝和深拷贝之间的区别,那么你会很轻松地掌握深度拷贝集合的机理。
可以很清楚地看到修改原有集合的Employee对象(即修改designation字段为"staff")也会反映到拷贝集合中,因为克隆是浅拷贝,它指向的是堆中同一个Employee对象。为了修复这个问题,我们需要遍历集合深度拷贝所有Employee对象,而在这之前,我们需要为Employee对象覆写clone方法。
1)让Employee类实现Cloneable接口;
2)在Employee类中添加下面的clone()方法;
3)使用下面的代码,用Java中深度拷贝取代拷贝构造函数;
4)为修改后的集合运行同样的代码,会得到不同的输出:
可以看出克隆后的和原有集合相互独立,分别指向不同的对象。
这便是Java如何克隆集合的所有内容,现在我们明白集合类中各种拷贝构造函数,如List或Set中addAll()方法,仅仅实现了集合的浅拷贝,即原有集合和拷贝的集合指向相同的对象。这也便要求在集合中存储任何对象,必须支持深度拷贝操作。
PS: 原Blog中的代码执行可能会遇到一些问题,但关键是这中间的思想很重要。
链接:
How to create Immutable Class and
Object in Java - Tutorial Example
Difference between deep copy vs Shadow Cloning
in Java
Collection interview questions answers
How clone method works in Java
程序员通常会误用集合类(如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字段,希望这个改变不会影响到拷贝集合,但事与愿违,解决这个问题的方法是深度拷贝集合类中的元素。import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 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 { private static final Logger logger = LoggerFactory.getLogger(CollectionCloningclass); public static void main(String args[]) { // deep cloning Collection in Java Collection org = new HashSet(); 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 copy = new HashSet(org); logger.debug("Original Collection {}", org); logger.debug("Copy of Collection {}", copy ); Iterator itr = org.iterator(); while(itr.hasNext()){ itr.next().setDesignation("staff"); } logger.debug("Original Collection after modification {}", org); logger.debug("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; } @Override public String toString() { return String.format("%s: %s", name, designation ); } }输出:
- 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]
可以很清楚地看到修改原有集合的Employee对象(即修改designation字段为"staff")也会反映到拷贝集合中,因为克隆是浅拷贝,它指向的是堆中同一个Employee对象。为了修复这个问题,我们需要遍历集合深度拷贝所有Employee对象,而在这之前,我们需要为Employee对象覆写clone方法。
1)让Employee类实现Cloneable接口;
2)在Employee类中添加下面的clone()方法;
@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)使用下面的代码,用Java中深度拷贝取代拷贝构造函数;
Collection<Employee> copy = new HashSet<Employee>(org.size()); Iterator<Employee> iterator = org.iterator(); while(iterator.hasNext()){ copy.add(iterator.next().clone()); }
4)为修改后的集合运行同样的代码,会得到不同的输出:
- 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()方法,仅仅实现了集合的浅拷贝,即原有集合和拷贝的集合指向相同的对象。这也便要求在集合中存储任何对象,必须支持深度拷贝操作。
PS: 原Blog中的代码执行可能会遇到一些问题,但关键是这中间的思想很重要。
链接:
How to create Immutable Class and
Object in Java - Tutorial Example
Difference between deep copy vs Shadow Cloning
in Java
Collection interview questions answers
How clone method works in Java
相关文章推荐
- 【转】Java如何克隆集合——深度拷贝ArrayList和HashSet
- Java中如何克隆集合——ArrayList和HashSet深拷贝
- Java中如何克隆集合——ArrayList和HashSet深拷贝
- Java中如何克隆集合——ArrayList和HashSet深拷贝
- Java中如何克隆集合——ArrayList和HashSet深拷贝
- Java中如何克隆集合——ArrayList和HashSet深拷贝
- Java中如何克隆集合——ArrayList和HashSet深拷贝
- Java中如何克隆集合——ArrayList和HashSet深拷贝
- 【Java基础之集合(一)】各种常用集合(Array、ArrayList、HashSet、HashMap、TreeMap等)分析对比
- Java基础---集合框架---迭代器、ListIterator、Vector中枚举、LinkedList、ArrayList、HashSet、TreeSet、二叉树、Comparator
- java集合——数组列表(ArrayList)+散列集(HashSet)
- Java基础---集合框架---迭代器、ListIterator、Vector中枚举、LinkedList、ArrayList、HashSet、TreeSet、二叉树、Comparator
- java中集合类HashSet、ArrayList、LinkedList总结
- Java集合--深度剖析Vector、Stack、ArrayList、LinkedList(二)
- Java集合HashSet-ArrayList-HashMap的线程同步控制方法和区别
- 浅析Java中的集合包(ArrayList,LinkedList,Vector, Stack,HashSet,TreeSet,HashMap,TreeMap)
- java集合(ArrayList,Vector,LinkedList,HashSet,TreeSet的功能详解)
- Java集合HashSet-ArrayList-HashMap的线程同步控制方法和区别
- Java集合HashSet-ArrayList-HashMap的线程同步控制方法和区别
- java:ArrayList集合与HashSet集合元素重复性探讨