Java-Vector vs ArrayList性能测试

Java-Vector vs ArrayList性能测试,java,performance,vector,arraylist,Java,Performance,Vector,Arraylist,每个人都说应该使用vector是因为它的性能(因为vector在每次操作和内容之后都会同步)。我写了一个简单的测试: import java.util.ArrayList; import java.util.Date; import java.util.Vector; public class ComparePerformance { public static void main(String[] args) { ArrayList<Integer> l

每个人都说应该使用vector是因为它的性能(因为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));
    }
}

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

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

  • 您必须预热代码
  • 您必须控制垃圾收集暂停
  • System.currentTimeMillis
    是不精确的,但您似乎连这个方法都不知道(您的
    new Date().getTime()
    是等效的,但速度较慢)
  • 如果您想正确地执行此操作,请查看Oracle的
    jmh
    工具或Google的卡钳

    我的测试结果 因为我有点想亲自看看这些数字,下面是
    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;
      }
    }
    
    注意以下几点:

    • …add
      方法中,我正在创建一个新的本地集合。JIT编译器使用了这一事实,避免了对
      Vector
      方法的锁定,因此性能几乎相同
    • …遍历
      方法中,我从一个全局集合中读取;锁不能被忽略,这就是真正的性能损失
      Vector

    主要的收获应该是:JVM上的性能模型非常复杂,有时甚至不稳定。从微基准点进行推断,即使非常小心,也可能导致对生产系统性能的危险错误预测。

    我做了您的测试,ArrayList比1000000大小的Vector更快

     public static void main(String[] args) {
                ArrayList<Integer> list = new ArrayList<Integer>();
                Vector<Integer> vector = new Vector<Integer>();
    
                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);
            }
        }   
    

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

    根据我的经验和我对实施的了解,我使用以下经验法则:

    • 使用ArrayList
    • 如果集合必须同步,则考虑向量的使用。(我从来没有使用过它,因为还有其他的同步、并发和并行编程解决方案)
    • 如果集合中有许多元素,并且列表中经常有插入或删除操作(不是在末尾),则使用LinkedList
    大多数集合不包含很多元素,花更多的精力来处理它们将是浪费时间。scala中还有并行集合,它们并行执行一些操作。也许在纯Java中也有可用的东西


    尽可能使用列表界面隐藏实现细节,并尝试添加注释,说明选择特定实现的原因。

    我同意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)");
        }
    }
    

    我认为这应该让您了解性能差异。

    1)不建议使用Vector 2)您的测试应该在2次内运行。。不是一个接一个你不知道JIT magic做什么,使用System.nanotime()所以,有两个注释:(1)由于同步,使用Vector会带来轻微的开销,这可能会使它比ArrayList慢。(2) 在这里,您实际上是在试验一个VM预热,它使ArrayList的µ-基准测试比Vector测试稍微慢一点。尝试交换两个基准测试,看看会发生什么!不知道你在哪里听到的,看看。也许您应该在
    C++
    中使用它,但不要在
    Java
    中使用它。请看,您的代码中似乎有一个输入错误,您在其中使用了向量遍历,非常感谢!如果我不早点做,我会“接受”你的@德斯特朗扎克很公平。在我写这篇文章的过程中,被接受的答案得到了改进,而且是最好的。
     public static void main(String[] args) {
                ArrayList<Integer> list = new ArrayList<Integer>();
                Vector<Integer> vector = new Vector<Integer>();
    
                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
    
    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)");
        }
    }
    
    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)