Java - Vector vs ArrayList性能 - 测试

dst*_*zak 6 java performance vector arraylist

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

import java.util.ArrayList;
import java.util.Date;
import java.util.Vector;

public class ComparePerformance {

    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        Vector<Integer> vector = new Vector<Integer>();

        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));
    }
}
Run Code Online (Sandbox Code Playgroud)

结果如下:

List time: 4360
Vector time: 4103
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*nik 27

你写了一个天真的微基准.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<Integer> 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<Integer> intList = Arrays.asList(ints);
  static Vector<Integer> vec = new Vector<Integer>(intList);
  static List<Integer> list = new ArrayList<Integer>(intList);

  @GenerateMicroBenchmark
  public Vector<Integer> testVectorAdd() {
    final Vector<Integer> v = new Vector<Integer>();
    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<Integer> testArrayListAdd() {
    final List<Integer> l = new ArrayList<Integer>();
    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;
  }
}
Run Code Online (Sandbox Code Playgroud)

结果如下:

testArrayListAdd          234.896  ops/msec
testVectorAdd             274.886  ops/msec
testArrayListTraverse    1718.711  ops/msec
testVectorTraverse         34.843  ops/msec
Run Code Online (Sandbox Code Playgroud)

请注意以下事项:

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

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


Sea*_*oyd 5

我同意 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<Integer> list;

        Run(final List<Integer> 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<Integer>()).perform();
            vectorTotal += new Run(new Vector<Integer>()).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)");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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)
Run Code Online (Sandbox Code Playgroud)

我会说这应该让您了解性能差异。