Java – Vector vs ArrayList性能 – 测试

每个人都说应该使用矢量因为性能(导致Vector在每次操作和东西之后同步)。 我写了一个简单的测试:

import java.util.ArrayList; import java.util.Date; import java.util.Vector; public class ComparePerformance { public static void main(String[] args) { ArrayList list = new ArrayList(); Vector vector = new Vector(); int size = 10000000; int listSum = 0; int vectorSum = 0; long startList = new Date().getTime(); for (int i = 0; i < size; i++) { list.add(new Integer(1)); } for (Integer integer : list) { listSum += integer; } long endList = new Date().getTime(); System.out.println("List time: " + (endList - startList)); long startVector = new Date().getTime(); for (int i = 0; i < size; i++) { vector.add(new Integer(1)); } for (Integer integer : list) { vectorSum += integer; } long endVector = new Date().getTime(); System.out.println("Vector time: " + (endVector - startVector)); } } 

结果如下:

 List time: 4360 Vector time: 4103 

基于此,似乎迭代和读取时的Vector性能略好一些。 也许这是一个愚蠢的问题,或者我做了错误的假设 – 有人可以解释一下吗?

你写了一个天真的微基准。 JVM上的微博技术是一项非常棘手的业务,它甚至不容易列举所有陷阱,但这里有一些经典的:

  1. 你必须热身代码;
  2. 你必须控制垃圾收集暂停;
  3. System.currentTimeMillis是不精确的,但你似乎并不知道甚至这个方法(你的new Date().getTime()是等效的,但更慢)。

如果您想要正确执行此操作,请查看Oracle的jmh工具或Google的Caliper。

我的测试结果

由于我有兴趣自己看这些数字,这里是jmh的输出。 一,测试代码:

 public class Benchmark1 { static Integer[] ints = new Integer[0]; static { final List list = new ArrayList(asList(1,2,3,4,5,6,7,8,9,10)); for (int i = 0; i < 5; i++) list.addAll(list); ints = list.toArray(ints); } static List intList = Arrays.asList(ints); static Vector vec = new Vector(intList); static List list = new ArrayList(intList); @GenerateMicroBenchmark public Vector testVectorAdd() { final Vector v = new Vector(); for (Integer i : ints) v.add(i); return v; } @GenerateMicroBenchmark public long testVectorTraverse() { long sum = (long)Math.random()*10; for (int i = 0; i < vec.size(); i++) sum += vec.get(i); return sum; } @GenerateMicroBenchmark public List testArrayListAdd() { final List l = new ArrayList(); for (Integer i : ints) l.add(i); return l; } @GenerateMicroBenchmark public long testArrayListTraverse() { long sum = (long)Math.random()*10; for (int i = 0; i < list.size(); i++) sum += list.get(i); return sum; } } 

结果如下:

 testArrayListAdd 234.896 ops/msec testVectorAdd 274.886 ops/msec testArrayListTraverse 1718.711 ops/msec testVectorTraverse 34.843 ops/msec 

请注意以下事项:

  • ...add方法我正在创建一个新的本地集合。 JIT编译器使用这个事实并省略了对Vector方法的锁定 - 因此几乎相同的性能;
  • ...traverse方法我正在从全球集合中读取; 锁不能被省略,这就是Vector的真正性能损失所在。

这主要应该是: JVM上的性能模型非常复杂,有时甚至是不稳定的 。 从微基准测量推断,即使在完全适当的情况下完成,也可能导致对生产系统性能的危险错误预测。

我同意Marko关于使用Caliper的看法,这是一个非常棒的框架。

但是,如果您更好地组织基准测试,您可以自己完成部分工作:

 public class ComparePerformance { private static final int SIZE = 1000000; private static final int RUNS = 500; private static final Integer ONE = Integer.valueOf(1); static class Run { private final List list; Run(final List list) { this.list = list; } public long perform() { long oldNanos = System.nanoTime(); for (int i = 0; i < SIZE; i++) { list.add(ONE); } return System.nanoTime() - oldNanos; } } public static void main(final String[] args) { long arrayListTotal = 0L; long vectorTotal = 0L; for (int i = 0; i < RUNS; i++) { if (i % 50 == 49) { System.out.println("Run " + (i + 1)); } arrayListTotal += new Run(new ArrayList()).perform(); vectorTotal += new Run(new Vector()).perform(); } System.out.println(); System.out.println("Runs: "+RUNS+", list size: "+SIZE); output(arrayListTotal, "List"); output(vectorTotal, "Vector"); } private static void output(final long value, final String name) { System.out.println(name + " total time: " + value + " (" + TimeUnit.NANOSECONDS.toMillis(value) + " " + "ms)"); long avg = value / RUNS; System.out.println(name + " average time: " + avg + " (" + TimeUnit.NANOSECONDS.toMillis(avg) + " " + "ms)"); } } 

关键部分通常是运行代码。 另外,删除与您的基准测试无关的内容。 重用Integers而不是创建新的Integers。

上面的基准代码在我的机器上创建了这个输出:

 Runs: 500, list size: 1000000 List total time: 3524708559 (3524 ms) List average time: 7049417 (7 ms) Vector total time: 6459070419 (6459 ms) Vector average time: 12918140 (12 ms) 

我想说应该让你了解性能差异。

正如Marko Topolnik所说,很难编写正确的微基准并正确解释结果。 有关这个主题的好文章可用。

根据我的经验以及我对实现的了解,我使用了这个经验法则:

  • 使用ArrayList
  • 如果必须同步集合,请考虑vector的用法。 (我从来没有最终使用它,因为还有其他同步,并发和并行编程的解决方案)
  • 如果集合中有许多元素,并且列表中有频繁的插入或删除操作(不在末尾),则使用LinkedList

大多数集合都不包含很多元素,花费更多精力对它们来说是浪费时间。 同样在scala中有并行集合,它们并行执行一些操作。 也许在纯Java中也可以使用某些东西。

尽可能使用List接口隐藏实现细节,并尝试添加注释,以显示您选择特定实现的原因。

我进行测试,ArrayList比Vector大1000000

  public static void main(String[] args) { ArrayList list = new ArrayList(); Vector vector = new Vector(); int size= 1000000; int listSum = 0; int vectorSum = 0; long startList = System.nanoTime(); for (int i = 0; i < size; i++) { list.add(Integer.valueOf(1)); } for (Integer integer : list) { listSum += integer; } long endList = System.nanoTime(); System.out.println("List time: " + (endList - startList)/1000000); // // long startVector = System.nanoTime(); // for (int i = 0; i < size; i++) { // vector.add(Integer.valueOf(1)); // } // for (Integer integer : list) { // vectorSum += integer; // } // long endVector = System.nanoTime(); // System.out.println("Vector time: " + (endVector - startVector)/1000000); } } 

输出运行不同的时间。

 Code : list time 83 vector time 113