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;i<buffer.length;i++) {
newBuffer[i] = buffer[i];
}
}
private 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");
}
}
執行結果如下所示:
但是在陣列資料比較多的時候,執行效率就不一定了:
結論:在陣列的長度不是很大的時候,基本遵循的規律: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);