數組複製速度 System.arraycopy()&clone() &Arrays.copyof() &for()探究

先申明覆制速度結果

  1. System.arraycopy()
  2. clone()
  3. Arrays.copyof()
  4. 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種方法對於對象都是淺複製

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章