目錄
前言
在Java中,如果A、B是基本的數據類型,可以用賦值的方式傳遞值。如果A、B是兩個相同類型的數組,那麼A=B相當於將數組A的引用傳遞給數組B;如果數組A發生改變,那麼引用數組B也要發生改變。
在 Java 中實現數組複製有 5 種方法:
- 【1】Arrays 類的 copyOf() 方法
- 【2】System 類的 arraycopy() 方法
- 【3】Arrays類的copyOfRange() 方法
- 【4】Object 類的 clone() 方法
- 【5】for循環
JDK版本:
- JDK_1.8.0_181
接下來,本節將逐一分析上面這幾種數組複製種方法的源碼,並對各自執行效率進行對比。
1、Arrays.copyOf()
首先,進入Arrays類中定義的copyOf()源碼:
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}
從源碼定義可以看出,copyOf()方法實際上調用的是System.arraycopy()方法。
- 語法格式:
- Arrays.copyOf(dataType[] srcArray, int length);
- 參數解釋:
- srcArray 表示要進行復制的數組;
- length 表示複製後的新數組的長度
OK,進入arraycopy()源碼一探究竟。
2、System.arraycopy()
arraycopy() 方法位於 java.lang.System 類中。看下它在源碼中是如何定義的:
//從指定的源數組開始複製數組指定位置,指向目標數組的指定位置。
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
arraycopy()方法是一個本地方法,方法對應的實現不在當前文件裏,而是在其他語言實現的的文件的,比如C、C++中。
- 語法格式:
- System.arraycopy(dataType[] srcArray, int srcIndex, int destArray, int destIndex, int length);
- 參數解釋:
- src表示源數組;
- srcPos表示源數組中的起始索引(包含);
- dest表示目標數組;
- destPos表示目標數組中的起始索引(不包含);
- length表示要複製的源數組長度。
- 注意:
- length + srcIndex的長度須 <= 源數組長度srcArray.length
- length+destIndex的長度須 <= 目標數組長度destArray.length
3、Arrays.copyOfRange()
copyOfRange() 方法位於Arrays類中。看下它在源碼中是如何定義的:
public static <T> T[] copyOfRange(T[] original, int from, int to) {
return copyOfRange(original, from, to, (Class<? extends T[]>) original.getClass());
}
public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
int newLength = to - from;
if (newLength < 0)
throw new IllegalArgumentException(from + " > " + to);
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
return copy;
}
從源碼中看,copyOfRange()方法實際上也是調用的是System.arraycopy()方法。
- 語法格式:
- Arrays.copyOfRange(dataType[] srcArray, int fromIndex, int enIndex);
- 參數解釋:
- srcArray表示源數組;
- fromIndex表示開始數組複製的起始索引,目標數組中將包含起始索引對應的元素,範圍:0 ~ srcArray.length ;
- endIndex表示開始數組複製結束索引,目標數組中將不包含終止索引對應的元素,範圍:大於fromIndex;
- 注意:
- 結束索引endIndex可以大於 srcArray.length,如果大於 srcArray.length,則目標數組中使用默認值填充。
這個方法相當於將源數組的一部分複製爲一個新的數組,最終返回新的數組。
4、Object.clone()
clone() 方法也可以實現複製數組。該方法是類 Object 中的方法,可以創建一個有單獨內存空間的對象。因爲數組也是一個 Object 類,因此也可以使用數組對象的 clone() 方法來複制數組。
看源碼定義:
protected native Object clone() throws CloneNotSupportedException;
clone() 方法是一個本地方法,返回值Object 類型,要使用強制類型轉換爲適當的類型。
- 語法格式:
- srcArray.clone();
5、for循環
for循環狂野粗暴,可以說能夠貫穿我們整個職業生涯,不再贅述。
6、執行效率比較
舉個栗子:
public class ArrayCopyTest {
public static void test_SystemArrayCopy(String[] srcArray) {
long start = System.nanoTime();
String[] destArray = new String[srcArray.length];
System.arraycopy(srcArray, 0, destArray, 0, srcArray.length);
long end = System.nanoTime();
System.out.println("System.arraycopy()方法耗時:" + (end - start) + " ms");
}
public static void test_ArraysCopyOf(String[] srcArray){
long start = System.nanoTime();
String[] destArray = new String[srcArray.length];
destArray = Arrays.copyOf(srcArray, 0);
long end = System.nanoTime();
System.out.println("Arrays.copyOf方法耗時:" + (end - start) + " ms");
}
public static void test_CopyOfRange(String[] srcArray){
long start = System.nanoTime();
String[] destArray = new String[srcArray.length];
destArray = Arrays.copyOfRange(srcArray,0,srcArray.length);
long end = System.nanoTime();
System.out.println("Arrays.copyOfRange()方法耗時:" + (end - start) + " ms");
}
public static void test_Clone(String[] srcArray){
long start = System.nanoTime();
String[] destArray = new String[srcArray.length];
destArray = srcArray.clone();
long end = System.nanoTime();
System.out.println("Object.clone()方法耗時:" + (end - start) + " ms");
}
public static void test_For(String[] srcArray){
long start = System.nanoTime();
String[] destArray = new String[srcArray.length];
for (int i = 0; i < srcArray.length; i++) {
destArray[i] = srcArray[i];
}
long end = System.nanoTime();
System.out.println("for循環複製數組耗時:" + (end - start) + " ms");
}
public static void main(String[] args) {
String[] srcArray = new String[100];
System.out.println("源數組長度:" + srcArray.length);
test_SystemArrayCopy(srcArray);
test_ArraysCopyOf(srcArray);
test_CopyOfRange(srcArray);
test_Clone(srcArray);
test_For(srcArray);
}
}
通過不斷地改變目標數組srcArray的長度,我們來觀察一下各自的執行效率(耗時)情況:
- 當srcArray.length = 100時
效率:for循環 > System.arraycopy() >Arrays.copyOfRange() > clone() > Arrays.copyOf()
- 當srcArray.length = 200時
效率:System.arraycopy() > for循環 > clone() > Arrays.copyOfRange() > Arrays.copyOf()
- 當srcArray.length = 1000時
效率:System.arraycopy() > clone() > Arrays.copyOfRange() > for循環 > Arrays.copyOf()
- 當srcArray.length = 2000時
效率:System.arraycopy() > clone() > Arrays.copyOfRange() > for循環 > Arrays.copyOf()
- 當srcArray.length = 10000時
效率:System.arraycopy() > clone() > Arrays.copyOfRange() > Arrays.copyOf() > for循環
- 當srcArray.length = 20000時
效率:System.arraycopy() > Arrays.copyOf() > clone() > Arrays.copyOfRange() > for循環
由上表測試結果可知,隨着數組長度不斷增多,System.arraycopy()方法的效率最高,而for循環的效率則隨着數組長度越來越大情況下,其執行效率有點刺眼睛。
7、小結
本節首先介紹了數組複製的幾種方式;然後針對這幾種方式的源碼逐一進行解讀,並簡單介紹了其語法格式和參數釋義;最後,通過針對這幾種數組複製方式給出了對應測試栗子不斷修改數組長度的方式對這幾種數組複製方法的效率進行比較。
- 數組複製的幾種方式:
- 【1】Arrays 類的 copyOf() 方法
- 【2】System 類的 arraycopy() 方法
- 【3】Arrays類的copyOfRange() 方法
- 【4】Object 類的 clone() 方法
- 【5】for循環
- 數組複製的幾種方法對應源碼解讀及語法格式簡述。
- 測試栗子,依次對各個方法進行比較,可知數組長度較小時,for循環執行效率顯著;當數組長度逐漸增大,甚至其長度過分大的時候,System.arraycopy()的效率優勢較爲顯著。
- 因此,在平時項目開發中,我們可以結合項目實地場景,及所需操作數組的長度,靈活地選擇數組複製方式,進而對於提升程序性能有意外的效果。