数组复制速度 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种方法对于对象都是浅复制

 

全部评论

相关推荐

过往烟沉:我说什么来着,java就业面就是广!
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务