System.arraycopy()的分析

首先我们分析一下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

 

 

 

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值