Java中System.arraycopy, Object.clone, Arrays.copyOf和for 4种数组复制方式的性能比较

用代码说话

package InterviewDirectory.binaryTree_example.System.arraycopy_Arrays;

import java.util.Arrays;

/**
 * Created by xxx on 2018/3/18.
 */
public class copyOf_clone_for {
    /**
     * System.arraycopy (浅拷贝)
     * Arrays.copyOf(浅拷贝),
     * Object.clone(对于对象而言,它是深拷贝,但是对于数组而言,它是浅拷贝)
     * for
     */
    private static final byte[] buffer = new byte[1024*10];
    static {
        for (int i = 0; i < buffer.length; i++) {
            buffer[i] = (byte) (i & 0xFF);
        }
    }
    private static long startTime;

    public static void main(String[] args) {
        startTime = System.nanoTime();
        methodFor();
        calcTime("methodFor");

        startTime = System.nanoTime();
        methodClone();
        calcTime("methodClone");

        startTime = System.nanoTime();
        methodArraysCopyOf();
        calcTime("methodArraysCopyOf");

        startTime = System.nanoTime();
        methodSystemArraycopy();
        calcTime("methodSystemArraycopy");
    }

    private static void methodFor() {
        byte[] newBuffer = new byte[buffer.length];
        for(int i=0;iprivate static void methodClone() {
        byte[] newBuffer = buffer.clone();
    }

    private static void methodArraysCopyOf() {
        byte[] newBuffer = Arrays.copyOf(buffer, buffer.length);
    }

    private static void methodSystemArraycopy() {
        byte[] newBuffer = new byte[buffer.length];
        System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
    }

    private static void calcTime(String method) {
        long endTime = System.nanoTime();
        System.out.println(method + " cost " +(endTime-startTime)+ " nanosecond");
    }
}

运行结果如下所示:
Java中System.arraycopy, Object.clone, Arrays.copyOf和for 4种数组复制方式的性能比较_第1张图片

但是在数组数据比较多的时候,运行效率就不一定了:
Java中System.arraycopy, Object.clone, Arrays.copyOf和for 4种数组复制方式的性能比较_第2张图片

结论:在数组的长度不是很大的时候,基本遵循的规律:System.arraycopy >Object.clone>Arrays.copyOf > for

原因总结:

(1)for循环拷贝(速度相对比较慢)
for的速度之所以最慢是因为下标表示法每次都从起点开始寻位到指定下标处(现代编译器应该对其有进行优化,改为指针),另外就是它每一次循环都要判断一次是否达到数组最大长度和进行一次额外的记录下标值的加法运算。

(2)Arrays.copyOf(浅拷贝)
查看Arrays.copyOf的源码可以发现,它其实本质上是调用了System.arraycopy。之所以时间差距比较大,是因为很大一部分开销全花在了Math.min函数上了。所以,相比之下,System.arraycopy效率要高一些。

public static byte[] copyOf(byte[] original, int newLength) {
    byte[] copy = new byte[newLength];
    System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
    return copy;
}

(3)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()方法去拷贝我们的类了。

  • 数组拷贝
    对于数组而言,它不是简单的将引用赋值为另外一个数组引用,而是创建一个新的数组。但是我们知道,对于数组本身而言,它它的元素是对象的时候,本来数组每个元素中保存的就是对象的引用,所以,拷贝过来的数组自然而言也是对象的引用,所以对于数组对象元素而言,它又是浅拷贝。我们用以下代码验证一下:
class Aby implements Cloneable{
    public int i;
    public Aby(int i) {
        this.i = i;
    }
    @Override
    public Object clone() throws CloneNotSupportedException {
        // TODO Auto-generated method stub
        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[] arr = {aby1,aby2};

        Aby[] arr2 = arr.clone();
        arr2[0].i = 3;
        System.out.println(arr[0].i);   //3
        System.out.println(arr2[0].i);  //3
    }
}

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

System.arraycopy()源码。可以看到是native方法:native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中,C++编写的 底层函数,为JDK的底层函数。 可以将native方法比作Java程序同C程序的接口,。

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

你可能感兴趣的:(java语言知识点,学习到新知识点)