从某些资源中,我发现在Java集合下,ArrayList
比Vector
更快。为了检查这一点,我尝试使用一个小程序,其中一个方法将一些数据添加到ArrayList
,然后检索这些数据,另一个方法向Vector
添加和检索相同的数据。现在main()
方法在两个不同的时间计算过程中调用其中两个,分别计算这些方法的执行时间。在结果中,我得到ArrayList
的执行时间是18385831 ns,而Vector
的执行时间是2190242 ns。我的项目负责人还说ArrayList
比Vector
快。但我的计划的结果是说了不同的东西。有谁可以请原因解释我正确的事情?如果ArrayList
真的比Vector
快。
这是我的源代码:
import java.util.*;
public class TestArraylist {
void Arraylistpart() {
ArrayList<Object> a1 = new ArrayList<Object>();
ArrayList<Object> a2 = new ArrayList<Object>();
a2 = a1;
a1.add(1);
a1.add('c');
a1.add("gh");
a1.add(2);
a1.set(2, "ab");
int count = 0;
System.out.println(a1);
try {
for (Object i : a1) {
a2.set(count, i.toString());
count = count + 1;
}
a2.sort(null);
System.out.println(a2);
} catch (ClassCastException e) {
System.err.println("Exception occurs");
}
}
void vectorpart() {
Vector<Object> v1 = new Vector<Object>();
Vector<Object> v2 = new Vector<Object>();
v2 = v1;
v1.add(1);
v1.add('c');
v1.add("ab");
v1.add(2);
int count1 = 0;
System.out.println(v1);
try {
for (Object i : v1) {
v2.setElementAt(i.toString(), count1);
count1 = count1 + 1;
}
v2.sort(null);
System.out.println(v2);
} catch (ClassCastException e) {
System.err.println("Exception occurs");
}
}
public static void main(String[] args) {
TestArraylist objct = new TestArraylist();
System.out.println("Arraylist program");
long startmili = System.currentTimeMillis();
long starttime = System.nanoTime();
objct.Arraylistpart();
long endtime = System.nanoTime();
long endmili = System.currentTimeMillis();
long totaltime = endtime - starttime;
long totaltimemili = endmili - startmili;
System.out.println(totaltime);
System.out.println("Time in mili is: " + totaltimemili);
System.out.println("\nVector program");
long startmili1 = System.currentTimeMillis();
long starttime1 = System.nanoTime();
objct.vectorpart();
long endtime1 = System.nanoTime();
long endmili1 = System.currentTimeMillis();
long totaltime1 = endtime1 - starttime1;
long totaltimemili1 = endmili1 - startmili1;
System.out.println(totaltime1);
System.out.println("Time in mili is: " + totaltimemili1);
}
}
答案 0 :(得分:2)
您的基准测试是错误的。首先,您不仅测量了基于阵列/矢量的操作的时间,而且还测量了打印的时间,这些打印的时间可能比其他所有操作的速度慢几个。其次,你没有进行预热,所以你的大部分代码都可能由解释器执行,而不是JIT编译。第三,在同一个JVM中启动两个测试,将它们置于不同的条件下:在第一次测试执行期间,JVM执行更多预热步骤(例如JIT编译),因此第一个测试从一开始就受到限制。让我们尝试使用JMH:
写一些等价物import org.openjdk.jmh.annotations.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Warmup(iterations = 20, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 20, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Fork(1)
@State(Scope.Benchmark)
public class TestArraylist {
@Benchmark
public String Arraylistpart() {
ArrayList<Object> a1 = new ArrayList<>();
ArrayList<Object> a2 = new ArrayList<>();
a2 = a1;
a1.add(1);
a1.add('c');
a1.add("gh");
a1.add(2);
a1.set(2, "ab");
int count = 0;
for (Object i : a1) {
a2.set(count, i.toString());
count = count + 1;
}
a2.sort(null);
return a1.toString()+a2.toString();
}
@Benchmark
public String vectorpart() {
Vector<Object> v1 = new Vector<>();
Vector<Object> v2 = new Vector<>();
v2 = v1;
v1.add(1);
v1.add('c');
v1.add("ab");
v1.add(2);
int count1 = 0;
for (Object i : v1) {
v2.setElementAt(i.toString(), count1);
count1 = count1 + 1;
}
v2.sort(null);
return v1.toString()+v2.toString();
}
}
我返回a1.toString()+a2.toString()
而不是打印以保留.toString()
次调用(在System.out.println
内的测试中完成)。
摘要结果如下:
Benchmark Mode Cnt Score Error Units
TestArraylist.Arraylistpart avgt 20 0,382 ± 0,003 us/op
TestArraylist.vectorpart avgt 20 0,421 ± 0,002 us/op
看,你的测试实际上表现得更快,更快。仅382纳秒和421纳秒。由于额外的同步,矢量确实有点慢。但似乎C2 JIT编译器在删除一些不必要的同步部分方面做得很好,因此时差不是很大。检查每次迭代统计数据也很有趣。对于ArrayList
:
# Warmup Iteration 1: 0,544 us/op
# Warmup Iteration 2: 0,471 us/op
# Warmup Iteration 3: 0,383 us/op
# Warmup Iteration 4: 0,377 us/op
# Warmup Iteration 5: 0,377 us/op
# Warmup Iteration 6: 0,373 us/op
...
Iteration 14: 0,374 us/op
Iteration 15: 0,376 us/op
Iteration 16: 0,381 us/op
Iteration 17: 0,376 us/op
Iteration 18: 0,379 us/op
Iteration 19: 0,383 us/op
Iteration 20: 0,385 us/op
Vector
:
# Warmup Iteration 1: 0,889 us/op
# Warmup Iteration 2: 0,630 us/op
# Warmup Iteration 3: 0,689 us/op
# Warmup Iteration 4: 0,662 us/op
# Warmup Iteration 5: 0,671 us/op
# Warmup Iteration 6: 0,673 us/op
# Warmup Iteration 7: 0,669 us/op
# Warmup Iteration 8: 0,657 us/op
# Warmup Iteration 9: 0,427 us/op
# Warmup Iteration 10: 0,421 us/op
# Warmup Iteration 11: 0,421 us/op
...
Iteration 17: 0,423 us/op
Iteration 18: 0,420 us/op
Iteration 19: 0,422 us/op
Iteration 20: 0,419 us/op
正如您所看到的,ArrayList
在迭代#3上达到了全速,而Vector
在迭代#9时达到了它。
答案 1 :(得分:0)
您的基准测试存在多种问题,使Vector
看起来比ArrayList
更快。要查看它已损坏,只需在执行Vector
基准测试之前执行ArrayList
benchmkar,然后突然得到相反的结果。
这里列出了在对Java代码进行基准测试时必须记住的事项:
预热代码 - 永远不要对冷代码进行基准测试: JIT优化器在运行时做了一些非常了不起的事情来优化Java代码。但只有在代码执行多次之后才会这样做。性能差异可能非常显着。因此,执行您的基准测试方法几次,而不是测量允许JIT编译器完成其工作的时间。它还确保您所需的所有类都已加载,并且此时间不计入运行时。你的基准测试不会这样做。
确保基准测试运行的时间足够长:在您的基准测试中,只有5个对象被添加到Vector
/ ArrayList
。此代码在大约1毫秒内执行,这太短,无法获得任何有用的结果。如果您的基准测试运行时间不够长,&#34;随机&#34;效果(即线程的调度)会过多地影响结果。尝试添加几百万个元素。
确保使用基准代码的结果:想象一下,您需要预测总计100万个值所需的时间,但您不会对结果做任何事情。 JIT优化器可能会发现您不需要它并丢弃整个计算,这使得基准测试无用。确保您使用结果,例如通过打印它。
如果你想真正认真对待它,还有一些事情要考虑,但这些可能是最重要的。
您可能希望了解一些有助于进行基准测试的框架,例如JMH。