数组复制速度 System.arraycopy()&clone() &Arrays.copyof() &for()探究
先申明复制速度结果
- 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种方法对于对象都是浅复制