0%

Java数组拷贝Benchmark


在《疯狂Java》的7.3,关于Object类的内容中,作者表示使用Object的默认clone方法比使用静态copy方法快两倍。

这一点十分的有趣,毕竟平常在写算法题的时候,经常会需要对数组进行拷贝,而我最常用的还是System.arraycopy()方法,那么难道Object.clone()真的要更快吗?

实践是检验真理的唯一标准,这里就对各种数组拷贝的方法进行比较,直接上代码比时间,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.util.Arrays;

public class ArrayCopyTest {
public static void main(String[] args) {
final int N = 10000;
final int M = 5000000;
long start, end;

int[] a = new int[N];
for (int i = 0; i < N; i++ ) {
a[i] = i;
}

start = System.currentTimeMillis();
for ( int i = 0; i < M; i++ ) {
int[] b = a.clone();
}
end = System.currentTimeMillis();
System.out.println("Object.clone()用时:" + (end - start) + "ms");

start = System.currentTimeMillis();
for ( int i = 0; i < M; i++ ) {
int[] c = new int[N];
System.arraycopy(a, 0, c, 0, N);
}
end = System.currentTimeMillis();
System.out.println("System.arraycopy()用时:" + (end - start) + "ms");

start = System.currentTimeMillis();
for ( int i = 0; i < M; i++ ) {
int[] c = new int[N];
for (int j = 0; j < N; j++ ) {
c[j] = a[j];
}
}
end = System.currentTimeMillis();
System.out.println("循环拷贝用时:" + (end - start) + "ms");

start = System.currentTimeMillis();
for ( int i = 0; i < M; i++ ) {
int[] c = Arrays.copyOf(a, N);
}
end = System.currentTimeMillis();
System.out.println("Arrays.copyOf()用时:" + (end - start) + "ms");
}
}

这里一共使用了四种数组拷贝的方法,Object.clone()System.arraycopy()for循环Arrays.copyOf()

测试结果1:

1
2
3
4
5
6
$ java ArrayCopyTest

Object.clone()用时:14491ms
System.arraycopy()用时:12859ms
循环拷贝用时:12700ms
Arrays.copyOf()用时:13938ms

测试结果2:

1
2
3
4
5
6
$ java ArrayCopyTest

Object.clone()用时:44249ms
System.arraycopy()用时:38115ms
循环拷贝用时:38092ms
Arrays.copyOf()用时:44240ms

…显然,这是完全没有想到的情况,上面是在两台不同的电脑上跑出来的结果,速度排序for循环>System.arraycopy()>Arrays.copyOf()>Object.clone()

不是说好的Object.clone()更快吗?这就算了。那么为了for循环竟然是最快的???


好吧,只能简单看一下源码了。

System.arraycopy():

1
2
3
public static native void arraycopy(Object src,  int  srcPos,
Object dest, int destPos,
int length);

Arrays.copyOf(),它实际调用的是copyOfRange():

1
2
3
4
5
6
7
8
9
10
11
12
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;
}

Object.clone():

1
protected native Object clone() throws CloneNotSupportedException;

可以发现,System.arraycopy()Object.clone()调用的都是native方法,Arrays.copyOf()调用的是System.arraycopy()

所以Arrays.copyOf()肯定是比System.arraycopy()慢。但是为什么这些方法都没有最简单的for循环快???

重新测试:

或许是刚才测试的顺序不对?

修改代码重新测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import java.util.Arrays;

public class ArrayCopyTest {

private static final int N = 100000;

private static final int M = 500000;

private static int[] a;

public static void main(String[] args) {

long a, b, c, d;
a = b = c = d = 0;

for (int i = 0; i < 10; i++) {
a += test1();
b += test2();
c += test3();
d += test4();
}

System.out.println("\n************************************\n");

System.out.println("Object.clone()总用时:" + a + "ms");
System.out.println("System.arraycopy()总用时:" + b + "ms");
System.out.println("循环拷贝总用时:" + c + "ms");
System.out.println("Arrays.copyOf()总用时:" + d + "ms");
}

private static long test1() {
long start, end;
start = System.currentTimeMillis();
for ( int i = 0; i < M; i++ ) {
a = new int[N];
int[] b = a.clone();
}
end = System.currentTimeMillis();
System.out.println("Object.clone()用时:" + (end - start) + "ms");
return end - start;
}


private static long test2() {
long start, end;
start = System.currentTimeMillis();
for ( int i = 0; i < M; i++ ) {
a = new int[N];
int[] c = new int[N];
System.arraycopy(a, 0, c, 0, N);
}
end = System.currentTimeMillis();
System.out.println("System.arraycopy()用时:" + (end - start) + "ms");
return end - start;
}

private static long test3() {
long start, end;
start = System.currentTimeMillis();
for ( int i = 0; i < M; i++ ) {
a = new int[N];
int[] c = new int[N];
for (int j = 0; j < N; j++ ) {
c[j] = a[j];
}
}
end = System.currentTimeMillis();
System.out.println("循环拷贝用时:" + (end - start) + "ms");
return end - start;
}


private static long test4() {
long start, end;
start = System.currentTimeMillis();
for ( int i = 0; i < M; i++ ) {
a = new int[N];
int[] c = Arrays.copyOf(a, N);
}
end = System.currentTimeMillis();
System.out.println("Arrays.copyOf()用时:" + (end - start) + "ms");
return end - start;
}

}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
$ java ArrayCopyTest

Object.clone()用时:24763ms
System.arraycopy()用时:23489ms
循环拷贝用时:20988ms
Arrays.copyOf()用时:21984ms
Object.clone()用时:23799ms
System.arraycopy()用时:23507ms
循环拷贝用时:24672ms
Arrays.copyOf()用时:22697ms
Object.clone()用时:23126ms
System.arraycopy()用时:21553ms
循环拷贝用时:23705ms
Arrays.copyOf()用时:21827ms
Object.clone()用时:21787ms
System.arraycopy()用时:21636ms
循环拷贝用时:23640ms
Arrays.copyOf()用时:21864ms
Object.clone()用时:22287ms
System.arraycopy()用时:23185ms
循环拷贝用时:25596ms
Arrays.copyOf()用时:22266ms
Object.clone()用时:22126ms
System.arraycopy()用时:22300ms
循环拷贝用时:25772ms
Arrays.copyOf()用时:22275ms
Object.clone()用时:22513ms
System.arraycopy()用时:22313ms
循环拷贝用时:23993ms
Arrays.copyOf()用时:22965ms
Object.clone()用时:22571ms
System.arraycopy()用时:22962ms
循环拷贝用时:24482ms
Arrays.copyOf()用时:22656ms
Object.clone()用时:22233ms
System.arraycopy()用时:23668ms
循环拷贝用时:25993ms
Arrays.copyOf()用时:23749ms
Object.clone()用时:22468ms
System.arraycopy()用时:22449ms
循环拷贝用时:24113ms
Arrays.copyOf()用时:22535ms

************************************

Object.clone()总用时:227673ms
System.arraycopy()总用时:227062ms
循环拷贝总用时:242954ms
Arrays.copyOf()总用时:224818ms

可以看到在第一轮的时候,循环拷贝最快,后面就变成最慢的了。

但是这里Arrays.copyOf()System.arraycopy()快。

算了,随便了,这三个其实速度都差不多,具体速度每次测得的结果都不一样,喜欢用哪一个用哪一个吧。