本文主要是介绍数组克隆及对象的深、浅克隆(deep clone、shallow clone),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
数组克隆及对象克隆的作用
在有的时候,我们需要分发出多个结构及内容相同,但各自间相互独立的实体,以用作业务需要(对于对象来说可能存在内部部分引用不独立的情况,此问题放在后面讨论)。比如说将数组 int[] body 或对象 Object body 拷贝出多份,分别命名为 body1,body2,body3,并且要求修改任何一个对象的时候其它对象均不会受到任何影响,此时就要用到克隆。
深克隆与浅克隆概念
先要说明的是,在java中数组被当作是对象来看待,同样也继承自Object类。
浅克隆(shallow clone)
被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
深克隆(deep clone)
被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
clone()方法
克隆数组有多种方法,而克隆对象则通过clone()方法来进行。同时,clone() 方法应当满足以下条件:
①对任何的对象body,都有body.clone() !=body//克隆对象与原对象不是同一个对象②对任何的对象body,都有body.clone().getClass()= =body.getClass()//克隆对象与原对象的类型一样
③如果对象body的equals()方法定义恰当,那么body.clone().equals(body)应该成立。
克隆数组
克隆数组至少有以下四种思路:
1、使用Object类的clone()方法, 这种方法最简单,得到原数组的一个副本。灵活形也最差。效率最差,尤其是在数组元素很大或者复制对象数组时;
2、使用Systems的arraycopy()这种方法速度最快,并且灵活性也较好,可以指定原数组名称、以及元素的开始位置、复制的元素的个数,目标数组名称、目标数组的位置;
3、Arrays类的copyOf()方法与copyOfRange()方法可实现对数组的复制;
4、使用循环结构。这种方法最灵活。唯一不足的地方可能就是代码较多。
在此还得先普及几个知识:
1、java中没有“二维数组”、“n纬数组”这种说法,因此java数组的概念并不同等于C中的数组。
在java中所谓的二维数组,应当叫做“数组的数组”。例如:
int arr[][] = {{111,222,333}, {444,555,666}, {777,888,999}};
那么,arr[0][0]~arr[0][2]则成为“该数组中的第0组数组”。
2、java数组并不一定是矩形的或者是正体结构的,java允许不规则的数组结构体存在。例如,我可以这样声明数组:
int arr[][] = {{111,222,333}, {666}, {777,888,999,11,22}};
此时该数组的内存分配将会是这样的:
如果我试图访问arr[1][1],必然会出现java.lang.ArrayIndexOutOfBoundsException异常。
使用Object.clone()进行克隆
我们通过一小段代码来展示:
public static void main(String[] args) {int arr[] = {0,1,2,3,4,5};int copyarr[] = null;copyarr = arr.clone();copyarr[2] = 2048;System.out.println("arr == copyarr? " + (arr == copyarr));System.out.println("arr[2] is " + arr[2]);System.out.println("copyarr[2] is " + copyarr[2]);}
该段代码输出结果为:
arr == copyarr? false
arr[2] is 2
copyarr[2] is 2048
显然,这就是我们所预期的结果。arr通过clone()方法克隆出了新的数组,使得arr与copyarr并不引用同一个对象,因此对copyarr所做出的任何更改都将不会影响到arr本身。
与普通循环赋值创建新数组对比,该方法效率相对较高,因为Object.clone()调用的是本地方法:
protected native Object clone() throws CloneNotSupportedException;
使用System.arraycopy()进行克隆
我们在刚才的例子上做少许改动:
public static void main(String[] args) {int arr[] = {0,1,2,3,4,5};int copyarr[] = new int[6];System.arraycopy(arr, 0, copyarr, 0, arr.length);copyarr[2] = 2048;System.out.println("arr == copyarr? " + (arr == copyarr));System.out.println("arr[2] is " + arr[2]);System.out.println("copyarr[2] is " + copyarr[2]);}
与Object.clone()不同,System.arraycopy()并不是返回一个新的数组,而是要求提供一个已经初始化的数组,并根据参数对新数组进行改动。查看调用方法,该方法亦是调用本地方法完成的:
public static native void arraycopy(Object src, int srcPos,Object dest, int destPos,int length);
使用Arrays.copyOf() / Arrays.copyOfRange()进行克隆
这两个方法无论使用的是哪个重载,最终都将是调用System.arraycopy()方法。Arrays类中的copy方法丰富及简化了拷贝的使用。
Arrays.copyOf()的其中一个重载方法:
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {T[] copy = ((Object)newType == (Object)Object[].class)? (T[]) new Object[newLength]: (T[]) Array.newInstance(newType.getComponentType(), newLength);System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));return copy;}
Arrays.copyOfRange()的其中一个重载方法:
public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {int newLength = to - from;if (newLength < 0)throw new IllegalArgumentException(from + " > " + to);T[] copy = ((Object)newType == (Object)Object[].class)? (T[]) new Object[newLength]: (T[]) Array.newInstance(newType.getComponentType(), newLength);System.arraycopy(original, from, copy, 0,Math.min(original.length - from, newLength));return copy;}
使用循环结构进行克隆
接下来是数组拷贝中最具探讨性的内容。
现在,我们来尝试克隆二维数组,使用的方法是上面所提到的克隆方法。代码如下:
public static void main(String[] args) {int arr[][] = {{111,222,333},{666}};int copyarr[][] = new int[2][3];copyarr = arr.clone();copyarr[0][0] = 9090;System.out.println("arr == copyarr? " + (arr == copyarr));System.out.println("arr[0] == b[0]? " + (arr[0] == copyarr[0]));System.out.println("arr[0][0] is " + arr[0][0]);System.out.println("b[0][0] is " + copyarr[0][0]);}
输出如下:
arr == copyarr? false
arr[0] == b[0]? true
arr[0][0] is 9090
b[0][0] is 9090
奇怪了,这似乎不是我们想要的结果!
我们发现,数组arr和copyarr并不是同一个对象,但是他们的子数组却指向同一个对象,因此导致了改变其中一个数组的值,导致另一个数组随之改变。
别忘了,在java中并没有二维数组的概念。每一个数组都是一个对象,而数组的数组,则是这个数组里面的一个对象而已。因此,如果你在类似上述例子的数组使用克隆,从而导致了只克隆了第一层而没有克隆内部数组,这种现象我们称之为浅克隆。请查看浅克隆的定义,在这里我们正好符合。
如果想要做到整个数组完全被克隆,则需要对数组中的数组进行克隆。同样,如果存在数组的数组的数组,那么则要继续深入内部进行克隆。这种情况下我们可以使用循环进行克隆,代码如下:
public static void main(String[] args) {int arr[][] = {{111,222,333},{666}};int copyarr[][] = new int[2][3];for (int i = 0; i < arr.length; i++) {copyarr[i] = arr[i].clone();}copyarr[0][0] = 9090;System.out.println("arr == copyarr? " + (arr == copyarr));System.out.println("arr[0] == copyarr[0]? " + (arr[0] == copyarr[0]));System.out.println("arr[0][0] is " + arr[0][0]);System.out.println("copyarr[0][0] is " + copyarr[0][0]);}
输出如下:
arr == copyarr? false
arr[0] == copyarr[0]? false
arr[0][0] is 111
copyarr[0][0] is 9090
数组arr与数组copyarr的全部引用均不指向同一个地址,这样我们便完成了深克隆。当然,如果你的数组结构极为复杂,则可以使用迭代方式进行循环,在此不进行演示。
以上部分内容转载或参考来源如下:
http://www.cppblog.com/baby-fly/archive/2010/11/16/133763.html?opt=admin
http://greemranqq.iteye.com/blog/1750028
在此表示感谢。
转载请注明来源,版权归原作者所有,未经同意严禁用于任何商业用途。
微博:http://weibo.com/theworldsong
邮箱:theworldsong@foxmail.com
这篇关于数组克隆及对象的深、浅克隆(deep clone、shallow clone)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!