矢量比ArrayList

时间:2017-04-25 05:24:10

标签: java arraylist vector collections

我正在阅读以下文章:

Understanding Collections and Thread Safety in Java

文章说:

  

你知道,Vector和Hashtable是Java历史早期存在的两个集合,它们从一开始就设计用于线程安全(如果你有机会查看它们的源代码,你会发现它们的方法都是同步的!)。但是,它们很快就会在多线程程序中暴露出糟糕的性能。您可能知道,同步需要锁,这些锁总是需要时间来监控,这会降低性能。

[我也使用Caliper做了基准测试;请听我这个]

还提供了示例代码:

public class CollectionsThreadSafeTest {

    public void testVector() {
        long startTime = System.currentTimeMillis();

        Vector<Integer> vector = new Vector<>();
        for (int i = 0; i < 10_000_000; i++) {
            vector.addElement(i);
        }

        long endTime = System.currentTimeMillis();
        long totalTime = endTime - startTime;
        System.out.println("Test Vector: " + totalTime + " ms");
    }

    public void testArrayList() {
        long startTime = System.currentTimeMillis();

        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10_000_000; i++) {
            list.add(i);
        }

        long endTime = System.currentTimeMillis();
        long totalTime = endTime - startTime;
        System.out.println("Test ArrayList: " + totalTime + " ms");
    }

    public static void main(String[] args) {
        CollectionsThreadSafeTest tester = new CollectionsThreadSafeTest();

        tester.testVector();
        tester.testArrayList();
    }
}

他们为上述代码提供的输出如下:

Test Vector: 9266 ms
Test ArrayList: 4588 ms

但是当我在我的机器上运行它时,它给了我以下结果:

Test Vector: 521 ms
Test ArrayList: 2273 ms

我发现这很奇怪。我认为做微观基准会更好。所以,我用caliper为上面写了一个基准。代码如下:

public class CollectionsThreadSafeTest extends SimpleBenchmark {

    public static final int ELEMENTS = 10_000_000;

    public void timeVector(int reps) {
        for (int i = 0; i < reps; i++) {
            Vector<Integer> vector = new Vector<>();
            for (int k = 0; k < ELEMENTS; k++) {
                vector.addElement(k);
            }
        }
    }

    public void timeArrayList(int reps) {
        for (int i = 0; i < reps; i++) {
            List<Integer> list = new ArrayList<>();
            for (int k = 0; k < ELEMENTS; k++) {
                list.add(k);
            }
        }
    }

    public static void main(String[] args) {
        String[] classesToTest = { CollectionsThreadSafeTest.class.getName() };
        Runner.main(classesToTest);
    }
}

但我得到了类似的结果:

 0% Scenario{vm=java, trial=0, benchmark=ArrayList} 111684174.60 ns; ?=18060504.25 ns @ 10 trials
50% Scenario{vm=java, trial=0, benchmark=Vector} 67701359.18 ns; ?=17924728.23 ns @ 10 trials

benchmark    ms linear runtime
ArrayList 111.7 ==============================
   Vector  67.7 ==================

vm: java
trial: 0

我有点困惑。这里发生了什么?我在这里做错了什么(这真的很尴尬)?

如果这是预期的行为,那么这背后的解释是什么?

更新#1

在阅读@Kayamananswer后,我通过更改VectorArrayList的初始容量值来运行卡尺测试。以下是时间(毫秒):

Initial Capacity    Vector  ArrayList
-------------------------------------
10_000_000          49.2    67.1
10_000_001          48.9    71.2
10_000_010          48.1    61.2
10_000_100          43.9    70.1
10_001_000          45.6    70.6
10_010_000          44.8    68.0
10_100_000          52.8    64.6
11_000_000          52.7    71.7
20_000_000          74.0    51.8
-------------------------------------

感谢所有输入:)

3 个答案:

答案 0 :(得分:3)

这里你并没有真正测试add()方法。您正在测试VectorArrayList增长的不同方式。当Vector已满时,ArrayList的大小会翻倍,但library(dplyr) f2a <- c(1,0,0,1) f2b <- c(0,0,0,1) f2c <- c(1,1,1,1) clustervar <- c("A","B","B","A") weight <- c(10,20,30,40) df <- data.frame (f2a, f2b, f2c, clustervar, weight, stringsAsFactors=FALSE) df 会有一些更精确的逻辑,以防止内部数组以指数方式增长并浪费内存。

如果您使用&gt;运行测试这两个类的初始容量为10000000,它们不需要调整大小,您只需要分析添加部分。

答案 1 :(得分:0)

在多线程环境中,矢量预计会变慢。在您的情况下,它应该是轻量级的。最好通过从10000个不同的线程中添加这些项来进行测试

答案 2 :(得分:-1)

ArrayList和Vector都有相同的添加方法:

ensureCapacity();
    elementData[elementCount++] = newElement;

差异只有一个。 Vector的add方法是同步的,而ArrayList则不是。理论上,同步方法比非同步方法慢。

要提高add方法的性能,您必须在构造函数中指定initialCapacity或调用方法ensureCapacity。只要您需要,就可以创建内部数组,因此无需重新创建它。