先申明覆制速度結果
- System.arraycopy()
- clone()
- Arrays.copyof()
- for()
背後原理探究
首先要申明的是這4種方法中的前3種是沒有本質區別的,對象都是淺複製(複製地址),而普通類型都是深複製(複製值),簡單來說
淺複製:複製地址,相當於複製了房子(內存數據)的鑰匙,一改全改
深複製:複製值,通俗意義上的拷貝,相當於建了一個一模一樣的房子(內存的數據)
接下來說說3種方式:
System.arraycopy()
源碼如下:
@HotSpotIntrinsicCandidate
public static native void arraycopy(Object src, int srcPos,Object dest,
int destPos,int length);
可以看到這是一個native(本地)方法,也就是說是用C++寫的,所以其效率比非native方法更高
但更要注意的是上面的註解@HotSpotIntrinsicCandidate,我們來看官方對這個註解的解釋
* The {@code @HotSpotIntrinsicCandidate} annotation is specific to the
* HotSpot Virtual Machine. It indicates that an annotated method
* may be (but is not guaranteed to be) intrinsified by the HotSpot VM. A method
* is intrinsified if the HotSpot VM replaces the annotated method with hand-written
* assembly and/or hand-written compiler IR -- a compiler intrinsic -- to improve
* performance. The {@code @HotSpotIntrinsicCandidate} annotation is internal to the
* Java libraries and is therefore not supposed to have any relevance for application
* code.
注意紅字粗體所說,爲了提升性能,在JVM裏對該註解的進行了手寫,這裏要提一個叫JNI(Java Native Interface)的東西,普通的native方法通俗的講就是編譯後還要通過JNI再次編譯成.cpp文件才能執行.而有 @HotSpotIntrinsicCandidate這個註解的方法在JVM裏就是用.cpp文件寫好的,所以就跳過了JNI階段,所以速度就能提升,這也是System.arraycopy()速度冠絕羣雄的原因.
Object.clone()
源碼如下:
@HotSpotIntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;
可以看到它也是native方法,也有@HotSpotIntrinsicCandidate註解,那爲啥速度比上面的大哥慢呢?這就要看到官方解釋的一句
It indicates that an annotated method may be (but is not guaranteed to be) intrinsified by the HotSpot VM
注意用詞:may be (but is not guaranteed to be),是的,clone()方法就是悲催的but,它並沒有被手工寫在JVM裏面,所以它不得不走JNI的路子,所以它就成了2哥。
Arrays.copyof()
源碼如下
@HotSpotIntrinsicCandidate
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;
}
3弟也有註解 @HotSpotIntrinsicCandidate,但它甚至不是native方法,所以這個註解也就是混子,也更印證了2哥的待遇,
而且可以很明顯的看到裏面本質是調用了大哥 System.arraycopy()來實現的,所以效率墊底也是妥妥的。
for()
這個就可以退出羣聊吧!for()無論是基本數據類型還是引用數據類型統統是深複製,而且其也不是封裝的函數,所以退出羣聊妥妥的。
最後上測試代碼,有興趣的可以看看:
package 問題;
import java.util.Arrays;
public class CopySpeed implements Cloneable {
/*
*
* 數組複製速度 1.System.arraycopy() 2.clone() 3.Arrays.copyof() 4.for()
*
*
*
*/
int i = 0;
String value = "123";
/*
* 基本數據類型測試
*
* */
public long[] CopySpeedTest(int[] t) {
/*
* 初始化所有
*
*/
int length = t.length;
int[] systemCopy = new int[t.length];
int[] clone = new int[t.length];
int[] arraysCopyOf = new int[t.length];
int[] For = new int[t.length];
/*
* 執行復制操作,並統計時間
*
*/
long begin = System.currentTimeMillis();
System.arraycopy(t, 0, systemCopy, 0, t.length);
long end = System.currentTimeMillis();
long arraycopyTimes = end - begin;
begin = System.currentTimeMillis();
clone = t.clone();
end = System.currentTimeMillis();
long cloneTimes = end - begin;
begin = System.currentTimeMillis();
arraysCopyOf = Arrays.copyOf(t, t.length);
end = System.currentTimeMillis();
long arraysCopyOfTimes = end - begin;
/*
* 爲了方便查看,這裏設定下面程序只執行一次
*
*/
if (i == 0) {
/*
* 查看哈希值
*/
System.out.println("t:\t" + t.hashCode());
System.out.println("systemCopy:\t" + systemCopy.hashCode());
System.out.println("clone:\t" + clone.hashCode());
System.out.println("arraysCopyOf:\t" + arraysCopyOf.hashCode());
i++;
}
/*
* 運行時間統計
*
*/
long[] times = new long[3];
times[0] = arraycopyTimes;
times[1] = cloneTimes;
times[2] = arraysCopyOfTimes;
return times;
}
/*
*
* 引用數據類型結果
*
* */
public long[] CopySpeedTest(CopySpeed[] t) {
/*
* 初始化所有
*
*/
int length = t.length;
CopySpeed[] systemCopy = new CopySpeed[length];
CopySpeed[] clone = new CopySpeed[length];
CopySpeed[] arraysCopyOf = new CopySpeed[length];
/*
* 執行復制操作,並統計時間
*
*/
long begin = System.currentTimeMillis();
System.arraycopy(t, 0, systemCopy, 0, t.length);
long end = System.currentTimeMillis();
long arraycopyTimes = end - begin;
begin = System.currentTimeMillis();
clone = t.clone();
end = System.currentTimeMillis();
long cloneTimes = end - begin;
begin = System.currentTimeMillis();
arraysCopyOf = Arrays.copyOf(t, t.length);
end = System.currentTimeMillis();
long arraysCopyOfTimes = end - begin;
/*
* 爲了方便查看,這裏設定下面程序只執行一次
*
*/
if (i == 1) {
/*
* 查看哈希值
*/
System.out.println("t[0]:\t" + t[0].hashCode());
System.out.println("systemCopy[0]:\t" + systemCopy[0].hashCode());
System.out.println("clone[0]:\t" + clone[0].hashCode());
System.out.println("arraysCopyOf[0]:\t" + arraysCopyOf[0].hashCode());
/*
* 修改新對象的值來判斷是淺複製還是深複製
*
*/
System.out.println("深淺複製判斷,以value屬性判斷");
System.out.println("修改前t[0]:\t" + t[0].value);
System.out.println("修改前systemCopy[0]:\t" + systemCopy[0].value);
System.out.println("修改前clone[0]:\t" + clone[0].value);
System.out.println("修改前arraysCopyOf[0]:\t" + arraysCopyOf[0].value);
System.out.println("---------------------------");
t[0].value = "t";
systemCopy[0].value = "systemCopy";
clone[0].value = "clone";
arraysCopyOf[0].value = "arraysCopyOf";
System.out.println("修改後t[0]:\t" + t[0].value);
System.out.println("修改後systemCopy[0]:\t" + systemCopy[0].value);
System.out.println("修改後clone[0]:\t" + clone[0].value);
System.out.println("修改後arraysCopyOf[0]:\t" + arraysCopyOf[0].value);
i++;
}
/*
* 運行時間統計
*/
long[] times = new long[3];
times[0] = arraycopyTimes;
times[1] = cloneTimes;
times[2] = arraysCopyOfTimes;
return times;
}
public static void main(String args[]) {
CopySpeed speed = new CopySpeed();
System.out.println("基本類型");
long[] baseTimes = new long[] { 0, 0, 0 };
int[] baseArrays = new int[10000000];
for (int i = 0; i < baseArrays.length; i++) {
baseArrays[i] = i;
}
for (int i = 0; i < 20; i++) {
// System.out.print(i+"次");
long[] temp = speed.CopySpeedTest(baseArrays);
baseTimes[0] += temp[0];
baseTimes[1] += temp[2];
baseTimes[2] += temp[1];
// System.out.println();
}
baseTimes[0] /= 20;
baseTimes[1] /= 20;
baseTimes[2] /= 20;
System.out.println(Arrays.toString(baseTimes));
System.out.println("引用類型");
long[] ObjectTimes = new long[] { 0, 0, 0 };
CopySpeed[] ObjectArrays = new CopySpeed[10000000];
for (int i = 0; i < ObjectArrays.length; i++) {
ObjectArrays[i] = new CopySpeed();
}
for (int i = 0; i < 20; i++) {
// System.out.print(i+"次");
long[] temp = speed.CopySpeedTest(ObjectArrays);
ObjectTimes[0] += temp[0];
ObjectTimes[1] += temp[1];
ObjectTimes[2] += temp[2];
// System.out.println();
}
ObjectTimes[0] /= 20;
ObjectTimes[1] /= 20;
ObjectTimes[2] /= 20;
System.out.println(Arrays.toString(ObjectTimes));
}
}
結果:
基本類型
t: 1552787810
systemCopy: 1361960727
clone: 739498517
arraysCopyOf: 125130493
[10, 14, 15]
引用類型
t[0]: 166239592
systemCopy[0]: 166239592
clone[0]: 166239592
arraysCopyOf[0]: 166239592
深淺複製判斷,以value屬性判斷
修改前t[0]: 123
修改前systemCopy[0]: 123
修改前clone[0]: 123
修改前arraysCopyOf[0]: 123
---------------------------
修改後t[0]: t
修改後systemCopy[0]: t
修改後clone[0]: t
修改後arraysCopyOf[0]: t
[22, 31, 55]
結果分析:
基本類型
t: 1552787810
systemCopy: 1361960727
clone: 739498517
arraysCopyOf: 125130493
[10, 14, 15]
對於基本數據類型可以看到4個對象的hashcode完全不同,也就驗證了前面的深複製的猜想
而10,14,15的運行時間也能證明他們的效率差別,當然因爲是基本數據類型,
而且數據量不算恐怖,所以差距並不大,特別是clone和arraysCopyOf
可以明顯的看到,clone因爲JNI的耽誤,而Arrays.CopyOf因爲System.Copy的加成速度也不賴。
引用類型
t[0]: 166239592
systemCopy[0]: 166239592
clone[0]: 166239592
arraysCopyOf[0]: 166239592
對於引用類型,額可以看到4個對象的hashcode值完全相同,說明指向的是同一塊內存
深淺複製判斷,以value屬性判斷
修改前t[0]: 123
修改前systemCopy[0]: 123
修改前clone[0]: 123
修改前arraysCopyOf[0]: 123
---------------------------
修改後t[0]: t
修改後systemCopy[0]: t
修改後clone[0]: t
修改後arraysCopyOf[0]: t
[22, 31, 55]
這裏以對象的屬性value做測試,可以看到t[0]裏面的數據更改後,所有的數據都更改了,
說明3種方法對於對象都是淺複製