开篇
在开发中你有没有遇到过一些关于集合复制的问题?
普通的集合复制只是将内存中栈的地址块拷贝一份,使得一个新的集合对象指向这个地址块,但是集合中的对象变量却是指向堆中的同一块区域。所以当拷贝的集合修改了自己集合对象内的数据时,源集合对象也随之改变了,这样的效果我们称之为Java集合对象的浅复制(即只是在栈中拷贝了,而堆中的数据并没有拷贝。)
而深复制则是同时将栈中和堆中的数据进行拷贝,这样其拷贝的集合和被拷贝的集合就没有任何关系了。
案例演示
@Data @AllArgsConstructor @NoArgsConstructor public class Demo { private int demoValue; } 复制代码
试验一下浅复制:
@Test public void testDemoCopy() { // 这里我创建了一个源集合 ArrayList<Demo> sourceCollection = new ArrayList<Demo>(); // 这里我向sourceCollection添加了一些对象 sourceCollection.add(new Demo(1)); sourceCollection.add(new Demo(2)); // 这里我创建了一个新的空集合 ArrayList<Demo> newCollection = new ArrayList<Demo>(); newCollection.addAll(sourceCollection); // 现在我修改了新集合中的一些对象 newCollection.get(0).setDemoValue(3); // 现在我们验证它在源集合中是什么 for(Demo demo : sourceCollection){ System.out.println(demo.getDemoValue()); } // 断言验证源Collection是否被修改. Assert.assertEquals(sourceCollection.get(0).getDemoValue(),1); } 复制代码
CollectionCopyjava @ Test public void testcomonCopyShallow // Here I create a source collection . ArrayList < Demo > sourceCollection = new ArrayList < Demo >(); // Here I add some objects to sourceCollection . sourceCollection . add ( new Demo (1)); sourceCollection . add ( new Demo (2)); // Here I create a new empty collection . ArrayList < Demo > newCollection = new ArrayList < Demo >(); newCollection . addAll ( sourceCollectiOn ); // Now I modify some objects in new collection . newCollection . get (0). setDemoValue (3); // Now We verify what it is inside the source collection . for ( Demo demo : sourceCollection ){ System . out . println ( demo . getDemoValue ()); // Now I verify if the source Collection is modified . Assert . assertEquals ( sourceCollection . get (0). getDemoValue (),1 @ Test olems Javadoc beclaration C Progress Console 3 avacollectionCopy . testcommonCopyShallow [ Unit ] D :SDKVRE1.8binjavaw. exe (20t
很明显,
newCollection
中改变的Demo
对象在SourceCollection
中也跟着改变了,这说明两个集合中的Demo
对象是同一个对象。这也是浅复制所存在的弊端。
那么如何将两个集合独立开来呢,即如何进行深度复制?
试验一下深复制:
首先我们先对Demo类作一下处理,使其实现Cloneable
接口,并重写它的clone
方法
protected Demo clone() throws CloneNotSupportedException { return (Demo)super.clone(); } 复制代码
测试类如下
@Test public void testCopyDeep() throws Exception{ ArrayList<Demo> sourceCollection = new ArrayList<Demo>(); sourceCollection.add(new Demo(1)); sourceCollection.add(new Demo(2)); ArrayList<Demo> newCollection = new ArrayList<Demo>(); for(Demo demo : sourceCollection){ // 这里是重点 newCollection.add(demo.clone()); } newCollection.get(0).setDemoValue(3); for(Demo demo : sourceCollection){ System.out.println(demo.getDemoValue()); } Assert.assertEquals(sourceCollection.get(0).getDemoValue(),1); } 复制代码
最后我们来观察一下结果:两个集合各自独立,谁修改都不会互相影响对象
collectionCopyjava // Now I verify if the source Collection is modified . Assert . assertEquals ( sourceCollection . get (0). getDemoValue (),1); @Te3t public void testCopyDeep () throws Exception ( ArrayList < Demo > sourceCollection = new ArrayList < Demo >(); sourceCollection . add ( new Demo (1)); sourceCollection . add ( new Demo (2)); ArrayList < Demo > newCollection = new ArrayList < Demo >(); for ( Demo demo : sourceCollection )( newCollection . add ( demo . clone ()); newCollection . get (0). setDemoValue (3); for ( Demo demo : sourceCollection ){ System . out . println ( demo . getDemoValue ()); Assert . assertEquals ( sourceCollection . get (0). getDemoValue (),1); Javadoc el Pd Progress acollectionCopy . testCopyDeep [ JUnit ] D :\SDKVRE1.8binjavaw. exe (2015年10月30日下午9:30:34) iaration Console
另一种快速copy集合的写法
现在很多人都用jdk8以上的版本了,那么上面这种复制集合的写法就显得有点臃肿了,可以使用java lambda表达式来进行语义上的优化,如下所示:
@Override public DebtDetailListDTO clone() throws CloneNotSupportedException { val cloned = (DebtDetailListDTO)super.clone(); cloned.data=data.stream().map(GwDebtDetail::clone).collect(Collectors.toList()); return cloned;
@Override public DebtDetailListDTO clone()throws CloneNotSupportedException { val cloned =(DebtDetailListDTO) super.clone(); cloned.data=data.stream().map(GwDebtDetail::clone).collect(Collectors.toList()) return cloned;