首先我们分析一下System.arraycopy()这个方法,下面是源码:
* @param src the source array.
* @param srcPos starting position in the source array.
* @param dest the destination array.
* @param destPos starting position in the destination data.
* @param length the number of array elements to be copied.
*/
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
System.arraycopy()一共有四个参数,每个参数的含义源码都有解释,下边我来用中文翻译一遍,让大家更能理解:
src:源数组 srcPos:源数组的起始位置 dest:目标数组 destPos:目标数组的起始位置 length:要复制数组元素的长度
一、深度复制和浅度复制的区别
Java数组的复制操作可以分为深度复制和浅度复制,简单来说深度复制,可以将对象的值和对象的内容复制;浅复制是指对对象引用的复制。
System中提供了一个native静态方法arraycopy(),可以使用这个方法来实现数组之间的复制。对于一维数组来说,这种复制属性值传递,修改副本不会影响原来的值。对于二维或者一维数组中存放的是对象时,复制结果是一维的引用变量传递给副本的一维数组,修改副本时,会影响原来的数组。
public class test {
public static void main(String[] args) {
User[] users = new User[]{new User(1, "syy"), new User(2, "cxk")};//初始化数组对象
User[] target = new User[users.length];//创建一个目标数组
System.arraycopy(users,0,target,0,users.length);
System.out.println("源数组和目标数组的物理地址是否一样:"+(users[0]==target[0]?"一样,浅复制":"不一样,深复制"));
}
}
class User{
private int id;
private String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
输出结果:
源数组和目标数组的物理地址是否一样:一样,浅复制
user = User{id=1, name='lh'}
user = User{id=2, name='cxk'}
对象复制的图示
所以,得出的结论是,System.arraycopy() 在拷贝数组的时候,采用的使用潜复制,复制结果是一维的引用变量传递给副本的一维数组,修改副本时,会影响原来的数组。
复制一维数组:
public static void main(String[] args) {
String[] s1=new String[]{"1","2","3","4","5"};//源数组
String[] s2 = new String[s1.length];//目标数组
System.arraycopy(s1,0,s2,0,s1.length);//数组开始复制
//改变目标数组的值
s2[0]="11";
s2[1]="22";
System.out.println("两个数组的地址是否一致:"+(s1[0]==s2[0]));
for(String str: s1){
System.out.print("s1 = " + str+"\t");
}
System.out.println();
for(String str:s2){
System.out.print("s2 = " + str+"\t");
}
}
结果:
两个数组的地址是否一致:false
s1 = 1 s1 = 2 s1 = 3 s1 = 4 s1 = 5
s2 = 11 s2 = 22 s2 = 3 s2 = 4 s2 = 5
我们修改了目标数组的值,但是很明显可以看到,源数组的值并没有被改变,所以我们可以知道:使用该方法复制数组时,目标数组的改变不会影响到源数组,这种复制方式属于值传递,修改副本不会改变原来的值。
我们继续分析:
public static void main(String[] args) {
String[] s1=new String[]{"1","2","3","4","5"};//源数组
String[] s2 = new String[s1.length];//目标数组
System.arraycopy(s1,0,s2,0,s1.length);//数组开始复制
System.out.println("两个数组的地址是否一致:"+(s1[0]==s2[0]));
for(String str: s1){
System.out.print("s1 = " + str+"\t");
}
System.out.println();
for(String str:s2){
System.out.print("s2 = " + str+"\t");
}
}
输出结果:
两个数组的地址是否一致:true
s1 = 1 s1 = 2 s1 = 3 s1 = 4 s1 = 5
s2 = 1 s2 = 2 s2 = 3 s2 = 4 s2 = 5
既然是值传递,为什么结果会是相等呢?
在System.arraycopy()进行复制的时候,首先检查了字符串常量池是否存在该字面量,一旦存在,则直接返回对应的内存地址,如不存在,则在内存中开辟空间保存对应的对象。
二维数组的复制:
String[][] s1 = {
{"A1","B1","C1","D1","E1"},
{"A2","B2","C2","D2","E2"},
{"A3","B3","C3","D3","E3"}
};
String[][] s2 = new String[s1.length][s1[0].length];
System.arraycopy(s1, 0, s2, 0, s2.length);
for(int i = 0;i < s1.length ;i++){
for(int j = 0; j< s1[0].length ;j++){
System.out.print(" " + s1[i][j] + " ");
}
System.out.println();
}
// A1 B1 C1 D1 E1
// A2 B2 C2 D2 E2
// A3 B3 C3 D3 E3
s2[0][0] = "V";
s2[0][1] = "X";
s2[0][2] = "Y";
s2[0][3] = "Z";
s2[0][4] = "U";
System.out.println("----修改值后----");
for(int i = 0;i < s1.length ;i++){
for(int j = 0; j< s1[0].length ;j++){
System.out.print(" " + s1[i][j] + " ");
}
System.out.println();
}
// Z Y X Z U
// A2 B2 C2 D2 E2
// A3 B3 C3 D3 E3
上述代码是对二维数组进行复制,数组的第一维装的是一个一维数组的引用,第二维里是元素数值。对二维数组进行复制后后,第一维的引用被复制给新数组的第一维,也就是两个数组的第一维都指向相同的“那些数组”。而这时改变其中任何一个数组的元素的值,其实都修改了“那些数组”的元素的值,所以原数组和新数组的元素值都一样了。
System.arraycopy()的效率,在这里,我们做一个测试案例:
public class test {
public static void main(String[] args) {
String[] srcArray = new String[1000000];
String[] forArray = new String[srcArray.length];
String[] arrayCopyArray = new String[srcArray.length];
//初始化数组
for(int index = 0 ; index < srcArray.length ; index ++){
srcArray[index] = String.valueOf(index);
}
long forStartTime = System.currentTimeMillis();
for(int index = 0 ; index < srcArray.length ; index ++){
forArray[index] = srcArray[index];
}
long forEndTime = System.currentTimeMillis();
System.out.println("for方式复制数组时间:" + (forEndTime - forStartTime));
long arrayCopyStartTime = System.currentTimeMillis();
System.arraycopy(srcArray,0,arrayCopyArray,0,srcArray.length);
long arrayCopyEndTime = System.currentTimeMillis();
System.out.println("System.arraycopy复制数组时间:" + (arrayCopyEndTime - arrayCopyStartTime));
}
}
在数组长度小的时候,两种方式相差无几,但是数据一多,System.arraycopy()的优势就体现出来了。
运行结果:
for方式复制数组时间:8
System.arraycopy复制数组时间:1