Java数组拷贝的四种方法

  • 循环赋值
  • System.arraycopy()
  • Arrays.copyOf()
  • Object.clone()
  • 对象拷贝
  • 数组拷贝
  • 复制引用和复制对象的区别


效率:System.arraycopy > clone > Arrays.copyOf > 循环赋值

Java数组拷贝的四种方法:

  • 循环赋值:速度相对较慢
  • System.arraycopy():浅拷贝
  • Arrays.copyOf():或者Arrays.copyOfRange(),浅拷贝
  • Object.clone()

循环赋值

循环拷贝其实没什么好说的啦,就是用一个for循环进行元素的逐个拷贝,进行深拷贝或者浅复制这个大家可以自己把握。

System.arraycopy()

这个是系统提供的拷贝方式,也是我们推荐使用的拷贝方式,它是浅拷贝,也就是说对于非基本类型而言,它拷贝的是对象的引用,而不是去新建一个新的对象。通过它的代码我们可以看到,这个方法不是用java语言写的,而是底层用c或者c++实现的,因而速度会比较快。

public static native void arraycopy(Object src, int srcPos,Object dest, int destPos,int length);

通过源代码我们可以看到,关键字native说明它不是用java语言写的,而是调用其他语言的代码。

  • src:源数组
  • srcPos:源数组中的起始位置
  • dest:目标数组
  • destPos:目标数组中的起始位置
  • length:要复制数组元素的数量
import java.util.Arrays;

public class Solution {
    public static void main(String[] args) throws CloneNotSupportedException {
        int[] src = new int[]{1, 2, 3, 4, 5};
        int[] dest = new int[]{11, 12, 13, 14, 15, 16};
        int srcPos = 2;
        int destPos = 3;
        int length = 2;
        System.arraycopy(src, srcPos, dest, destPos, length);
        System.out.println(Arrays.toString(dest));//[11, 12, 13, 3, 4, 16]
    }
}

Arrays.copyOf()

这个方法也是浅拷贝,为什么呢?我们看一下它的源代码就知道了。

public static byte[] copyOfRange(byte[] original, int from, int to) {                   
   int newLength = to - from;
   if (newLength < 0)
       throw new IllegalArgumentException(from + " > " + to);
       byte[] copy = new byte[newLength];
       System.arraycopy(original, from, copy, 0,
              Math.min(original.length - from, newLength));
        return copy;
    }

实际上它调用的就是System.arraycopy,所以肯定也是浅拷贝。

Object.clone()

clone()比较特殊,对于对象而言,它是深拷贝,但是对于数组而言,它是浅拷贝。

对象拷贝

首先讲一下对象的拷贝,它是深拷贝,大家可以用对象去测试一下。下面我们看一下它的源代码:

protected native Object clone() throws CloneNotSupportedException;

这里也有native关键字,所以也是底层的c语言实现的。
还要注意的是,这里修饰符是protected,也就是说,我们创建了一个Object类以后,是不能直接调用这个clone()方法的,因为protected关键字只允许同一个包内的类和它的子类调用,所以我们声明一个object类时,肯定不是同一个包内,所以就不能去调用它。

要调用这个方法,就需要我们写一个类,然后声明实现cloneable接口就好了,不需要去显示地声明继承于object,因为java中的类如果不显示说明父类的话,默认父类就是object。然后我们继承这个方法:

@Override
    public Object clone() throws CloneNotSupportedException {
        // TODO Auto-generated method stub
        return super.clone();
    }

这里需要是,为了能够在不同包内去调用这个方法,我们需要把这个权限升级为public。现在我们就可以调用这个类的clone()方法去拷贝我们的类了。

public class Solution {
    public static void main(String[] args) throws CloneNotSupportedException {
        Cat cat1 = new Cat("abc", 3);
        System.out.println(cat1);//Cat@1b6d3586
        Cat cat2 = (Cat) cat1.clone();
        System.out.println(cat2);//Cat@4554617c
    }
}

//被克隆的类要实现Cloneable接口
class Cat implements Cloneable {
    private String name;
    private int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

数组拷贝

对于数组而言,它不是简单的将引用赋值为另外一个数组引用,而是创建一个新的数组。但是我们知道,对于数组本身而言,它它的元素是对象的时候,本来数组每个元素中保存的就是对象的引用,所以,拷贝过来的数组自然而言也是对象的引用,所以对于数组对象元素而言,它又是浅拷贝。我们用以下代码验证一下:

public class Solution {
    public static void main(String[] args) throws CloneNotSupportedException {
        Aby aby1 = new Aby(1);
        Aby aby2 = (Aby) aby1.clone();
        aby1.i = 2;
        System.out.println(aby1.i);//2
        System.out.println(aby2.i);//1

        Aby[] arr1 = {aby1, aby2};
        Aby[] arr2 = arr1.clone();
        arr2[0].i = 3;

        System.out.println(arr1[0].i);//3
        System.out.println(arr2[0].i);//3
    }
}

class Aby implements Cloneable {
    public int i;

    public Aby(int i) {
        this.i = i;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

复制引用和复制对象的区别

复制引用:是指将某个对象的地址复制,所以复制后的对象副本的地址和源对象相同,这样,当改变副本的某个值后,源对象值也被改变;

复制对象:是将源对象整个复制,对象副本和源对象的地址并不相同,当改变副本的某个值后,源对象值不会改变;

public class Solution {
    public static void main(String[] args) throws CloneNotSupportedException {
        Cat cat1 = new Cat("abc", 3);//源对象
        System.out.println("源对象地址:" + cat1);//源对象地址:Cat@1b6d3586
        //调用clone方法,复制对象
        Cat cat2 = (Cat) cat1.clone();
        System.out.println("复制对象地址:" + cat2);//复制对象地址:Cat@4554617c
        //复制引用
        Cat cat3 = cat1;
        System.out.println("复制引用地址:" + cat3);//复制引用地址:Cat@1b6d3586
    }
}

//被克隆的类要实现Cloneable接口
class Cat implements Cloneable {
    private String name;
    private int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}