java中的拷贝,深拷贝,浅拷贝(提前mark)

clone

今儿在刷LeetCode时,因为忘记了java中的引用类型变量的复制是复制引用的,而不是复制值的,于是便像之前一样复制基本类型变量一样来复制引用类型变量。

results.add(result)

这里results是List<List<Integer>>的,而result是List<Integer>的。在代码中,有一个回溯过程,会不断对result进行修改,然后进行判断,判断成功后将result加入results中去。
所以最终results中的值是[result1,result2,result3,result4,result5,result6],这里用1-6来表示result每次加入results的值都是不一样的。
但是结果却是不对的,我在这行代码前将result输出出来,然后最终又输出results。结果如下
在这里插入图片描述
(前6行是不断变化的result,最后一行是results)
每次result都是有值的,但最后results的结果却是空的,这是为什么呢?

这样写对于基本类型是没有问题的,但是对于引用类型是不一样。
因为results并不是如我所想的[result1,result2,result3,result4,result5,result6]这样简单,因为是引用类型,所以每次加入进去results中的并不是result的值,而是result的引用,所以正确的result的内容是[result,result,result,result,result,result],在整体的代码进行完回溯后result==[],所以results==[[],[],[],[],[],[]]。如此便能解释的通。

所以在多次将一个引用类型的变量加入进List时,或者whatever其他容器时,都要进行拷贝,不然最后你会发现,多次加入进去的值都只是最后一次,与你所想不同。虽然这很简单,但是还是经常容易忘,一旦忘记,就很致命。

这里对代码进行修改。修改如下

results.add((List<Integer>)result.clone());

这里是使用了clone,他是object的方法,用于将原对象拷贝出一个新对象来,于是这样,便能够符合[result1,result2,result3,result4,result5,result6]的想法了。
除此之外,这里要注意一点。

对于List

List<xxxx> list= new xxxxxx<>();

是不能够使用clone方法的,使用之后会报clone is protected类似的错误。
而用ArrayList或者LinkedList是可以使用clone方法的

ArrayList<xxxx> list = new xxxxxx<>();

这里需要注意一下。(可能需要进行强制类型转换,因为上线要求的可能是List<>)

更改了代码,结果就正常了
在这里插入图片描述

深拷贝与浅拷贝

在遇到这个问题之后,我进行了相关的查询,查到一个好玩的东西,深拷贝与浅拷贝,由于暂时不需要接触这个,所以就把我看的博客给总结一下,提前mark一下,我觉得不久我是会遇到使用场景的,到时候还得回本文看一看,改一改。

首先,对于拷贝,无论是深拷贝还是浅拷贝,都是拷贝。
它们都是根据原对象来创建一个新的对象。(就像我们上面的result.clone()一样)

然而深拷贝和浅拷贝是有区别的。
比如上面的ArrayList.clone()就是浅拷贝,浅拷贝在于,它只是根据原对象A创建了一个新对象A*,而假如原对象中的字段里有引用类型对象b,那么,新对象中的同一位置放的也是b,它并没有创建一个新的对象b*。
而深拷贝则相反,它创建了新的b*。

所以深拷贝与浅拷贝的区别基本就在于其拷贝的深浅,一个在于将对象中的所有对象都拷贝了,很深,另外一个只考虑原对象,一旦原对象里嵌套了引用类型,那它的新对象与原对象则藕断丝连,这种拷贝可以理解成比较肤浅或者表面。

然后深拷贝的实现方式有啥序列化还有什么的,这里之后会再来补充。

猜你喜欢

转载自blog.csdn.net/qq_34687559/article/details/109091290