在有的时候,我们需要分发出多个结构及内容相同,但各自间相互独立的实体,以用作业务需要(对于对象来说可能存在内部部分引用不独立的情况,此问题放在后面讨论)。比如说将数组 int[] body 或对象 Object body 拷贝出多份,分别命名为 body1,body2,body3,并且要求修改任何一个对象的时候其它对象均不会受到任何影响,此时就要用到克隆。
先要说明的是,在java中数组被当作是对象来看待,同样也继承自Object类。
被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
克隆数组有多种方法,而克隆对象则通过clone()方法来进行。同时,clone() 方法应当满足以下条件:
①对任何的对象body,都有body.clone() !=body//克隆对象与原对象不是同一个对象③如果对象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异常。
我们通过一小段代码来展示:
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;
我们在刚才的例子上做少许改动:
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);
这两个方法无论使用的是哪个重载,最终都将是调用System.arraycopy()方法。Arrays类中的copy方法丰富及简化了拷贝的使用。
Arrays.copyOf()的其中一个重载方法:
public static 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[] 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
邮箱:[email protected]