我运行了两个测试用例(多次),似乎迭代地向我的列表添加值比使用addAll
String[] rawArgs = new String[]{"one", "two", "three", "four", "five"};
// More efficient - 894 ns
List<String> list = new ArrayList<>();
for (String s : rawArgs) {
list.add(s);
}
// Less efficient - 1340 ns
List<String> list = new ArrayList<>();
list.addAll(Arrays.asList(rawArgs));
我通过我的IDE以及其他人获得笔记,后一种方法是将数组转换为该数据结构的“正确”方式。但如果它实际上比第一个慢,那么有什么优势(一些模糊的类型安全?),我应该使用第二个?
修改 - 代码基准测试:
JVM预热,首先重新创建主类对象:
public static void main(String[] args) {
Internet test;
for (int i = 0; i < 15; i++) {
test = new Internet(); // JVM warmup
}
test = new Internet();
test.printOutput();
}
我只是在操作的两端采用纳米级系统:
start = System.nanoTime();
/* function */
end = System.nanoTime();
result = end - start;
在测试用例中,每个开始/结束都有单独的字段,并且在操作后计算结果(在运行测试之前,通过循环实例也可以预先加热JVM。)
编辑2 - 使用更大的馆藏进行基准测试
经过一些测试(使用Integer而不是手写所有数字),看起来更大的集合确实更慢:
有100个数字:
First operation: 18759ns
Second operation: 2680ns
Total operation: 21439ns
答案 0 :(得分:18)
for-each循环解析为等效于
for (int i = 0; i < rawArgs.length; i++) {
list.add(rawArgs[i]);
}
... ArrayList.addAll
的实现实际上调用了toArray()
,因此它最终调用Arrays.asList(rawArgs).toArray()
,这会产生一个冗余副本。也就是说,它也会执行System.arraycopy
,这可能最终使它比for循环更快 - 它可以采用任何一种方式,并且根据其他一些基准测试,它实际上可能在不同的上下文中有所不同。
Collections.addAll(Collection<E>, E...)
静态方法实际上旨在解决此特定问题,并且比addAll(Arrays.asList))
更快,正如其Javadoc中明确说明的那样。
答案 1 :(得分:6)
我认为Collection.addAll()
更快,至少有两个原因:
ArrayList
ensureCapacityInternal(size + numNew);
因此,如果添加的List很大,并且在手动添加中,它将被多次调用,但在这种情况下,它将被调用一次,具有必要的容量。System.arraycopy(a, 0, elementData, size, numNew);
方法进行复制,这是一种原生的高性能方法。答案 2 :(得分:4)
这是一个基准测试,阵列中的元素略多一些。结果清楚地表明addAll
方法赢得了保证金:
public static void main(String[] args) {
String[] rawArgs = new String[]{"one", "two", "three", "four", "five",
"one", "two", "three", "four", "five",
"one", "two", "three", "four", "five",
"one", "two", "three", "four", "five",
"one", "two", "three", "four", "five",
"one", "two", "three", "four", "five",
"one", "two", "three", "four", "five",
"one", "two", "three", "four", "five"};
/******** WARM UP JVM *********/
for (int i = 0; i < 1000; ++i) {
arrayToListLoop(rawArgs);
}
for (int i = 0; i < 1000; ++i) {
arrayToListAddAll(rawArgs);
}
/** Actual measurement **/
long start = System.nanoTime();
for (int i = 0; i < 1000; ++i) {
arrayToListLoop(rawArgs);
}
long end = System.nanoTime();
System.out.println((end - start) / 1000);
start = System.nanoTime();
for (int i = 0; i < 1000; ++i) {
arrayToListAddAll(rawArgs);
}
end = System.nanoTime();
System.out.println((end - start) / 1000);
}
public static void arrayToListLoop(String[] arr) {
List<String> list = new ArrayList<>();
for (String s : arr) {
list.add(s);
}
}
public static void arrayToListAddAll(String[] arr) {
List<String> list = new ArrayList<>();
list.addAll(Arrays.asList(arr));
}
<强>结果:强>
1首次运行:
2280
812
2 Second Run:
1336
613
3第三次运行:
2088
751
答案 3 :(得分:2)
我尝试用大量的迭代重复这个实验,并且给你带来了不同的结果:
public static void main(String[] args) {
String[] rawArgs = new String[]{"one", "two", "three", "four", "five"};
long start = System.nanoTime();
for (int i = 0; i < 10000000; i++) {
List<String> list = new ArrayList<>();
for (String s : rawArgs) {
list.add(s);
}
}
long end = System.nanoTime();
System.out.println("add(): " + (end - start));
start = System.nanoTime();
for (int i = 0; i < 10000000; i++) {
List<String> list = new ArrayList<>();
list.addAll(Arrays.asList(rawArgs));
}
end = System.nanoTime();
System.out.println("addAll(): " + (end - start));
}
结果:
add(): 310726674
addAll(): 233785566
确切的数字会有所不同,但我的JVM(在Windows上运行的Sun JDK 1.7)上addAll
始终总是。两者运行的顺序也没有区别(我试过两种方式),所以这与预热无关。如果增加元素数量,结果会更加显着,可能是因为ArrayList后面的数组必须调整大小(因此需要额外的arraycopy)。
答案 4 :(得分:1)
有两种计时基准的方法+正如其他人所说,你应该在进行真正的计算之前预热JVM。我个人从未决定基准时间何时不到1秒,我试图创建一个更大的问题来查看实际结果。所以,
如果您的时间太小(某些ns或ms),则必须增加问题的大小,例如在您的情况下添加N个元素,例如N> 1000。
int size = 10000;
String[] rawArgs = new String[size];
//add some elements for this test
for (int i=0; i<size; i++) {
rawArgs[i] = String.valueOf(i);
}
要测试较小的尺寸,您必须预热JVM(在实际测量之前进行一些测试测量)并创建一个循环来测量问题的多次运行,然后将总时间除以这些循环重复次数。例如,
//after the warm up try the following
int repetitions = 1000;
start = System.nanoTime();
for (int i=0; i<repetitions; i++) {
//your calculations
}
end = System.nanoTime();
System.out.println("Cost per repetition: " + (end - start)/repetitions);