【问题标题】:Time comparison of add method for: ArrayList, LinkedList, ArrayList (initialized with some value)add 方法的时间比较:ArrayList、LinkedList、ArrayList(用某个值初始化)
【发布时间】:2013-12-23 23:21:09
【问题描述】:

我想了解更多关于数据结构及其实现的信息(用 JAVA 语言)。今天我写了一个测试来比较(时间比较)ADT List 的不同实现。具体我比较了add方法,这是我的测试:

@Test
public void testTime() {
    long i = 10000000;
    long INITIALIZED_VALUE=5000000;
    List<Integer> arrayBasedList = new ArrayList<Integer>();
    List<Integer> linkedBasedList = new LinkedList<Integer>();
    List<Integer> arrayBasedInitialedSizeList = new ArrayList<Integer> (INITIALIZED_VALUE);

    long t1 = System.currentTimeMillis();
    for (int index = 0; index <= i; index++) {
        arrayBasedList.add(index);
    }
    long t1End = System.currentTimeMillis() - t1;

    long t2 = System.currentTimeMillis();

    for (int index = 0; index <= i; index++) {
        linkedBasedList.add(index);
    }
    long t2End = System.currentTimeMillis() - t2;

    long t3 = System.currentTimeMillis();
    for (int index = 0; index <= i; index++) {
        arrayBasedInitialedSizeList.add(index);
    }
    long t3End = System.currentTimeMillis() - t3;

    System.out.println("ArrayBased: " + t1End);
    System.out.println("LinkedList:" + t2End);
    System.out.println("ArrayBasedInitializedSize: " + t3End);

    System.out.println("End");
}

我得到了这个结果:

ArrayBased: 5681 LinkedList:12830 ArrayBasedInitializedSize: 858

为什么 LinkedList 比 ArrayList 实现慢?我认为用于 LinkedList 实现的 add 方法比用于 Array 实现的 add 方法快。

谁能解释一下为什么数组的add方法比linkedlist快?

谢谢

阿莱西奥

【问题讨论】:

    标签: java performance list arraylist linked-list


    【解决方案1】:

    这里有两件事。

    首先是您的时间不可靠,因为您没有在每次运行之前“预热”代码。 Java 在运行时优化和编译代码,因此前几次通过它比以后运行慢得多。你应该运行你的测试循环几百次,把这些结果扔掉,然后做计时。

    回答这个问题:

    LinkedList 是恒定的时间,无论列表有多长,但每次添加它都必须做更多的工作。它需要创建包装对象,将其插入列表,更新引用等。

    另一方面,ArrayList 只是在数组中设置一个值,然后递增大小计数器。只有当它需要重新分配数组时,它才需要做很多工作。

    在开始时添加新对象进行测试并比较结果,您会发现事情更倾向于链表,因为现在ArrayList 每次都需要重新排列所有值。

    在您的第三个测试中也说明了重新分配数组的成本,通过提前知道 ArrayList 的大小,它变得更加高效。

    大 O 表示法在讨论算法时很有帮助,但您需要了解它的实际含义,否则可能会产生误导。它谈论的是操作的顺序,而不是实际需要多长时间。对于大多数追加插入,ArrayListLinkedList 都是 O(1),如果需要重新分配,ArrayList 插入是 O(n)。

    所有 O(1) 都表明,无论列表中有多少对象,它仍然需要相同的时间。在 10 项列表、100 项列表、10000 项列表的末尾添加一些内容仍然需要相同的时间。

    LinkedList 虽然比 ArrayList 花费更多时间 - 即使它们仍然具有相同的 O(1) 顺序。

    事实上,速度差异是这样的,即使您查看添加到 LinkedList 为 O(1) 且 ArrayList 为 O(n) 的列表的开头,对于小型列表,ArrayList 仍然更快!

    给出一些想法(这只是一个示例,不要尝试从中获取确切的时间!)然后如果添加 LinkedList 所需的时间是 L 并且 ArrayList 的时间是 A 那么总最后做加法的时间是 L*1, A*1。开头加的是L*1, A*N。

    因此,如果 L/A

    (如果您需要随机访问读取,链表也是 O(n),这也是一个很大的因素)。

    【讨论】:

    • 嗨蒂姆,感谢您的回答。我按照你的建议重复测试。在最坏的情况下,我们如何通过 Big O 表示法来表示在 Array 实现中添加方法的时间?
    • 对不起,我的意思是最坏的情况。
    • 伟大的蒂姆,我明白你在说什么,我把我的想法解释得很糟糕,这与你的想法非常相似。但是你给出了很好的解释。谢谢!
    【解决方案2】:

    我对您的结果感到惊讶,并尝试自己运行您的代码。这是我的输出:

    ArrayBased: 5589
    LinkedList:1219
    ArrayBasedInitializedSize: 789
    End
    

    我认为,LinkedList 的 add 方法应该比 ArrayList 慢一点(都是 O(1),但常数不同)。它们都创建新的 Integer 对象,而 ArrayList 只是将它们放入数组中,而 LinkedList 则创建链接。

    ArrayBased 和 ArrayBasedInitializedSize 的区别很明显:数组重新分配和元素复制需要太多时间。

    【讨论】:

    • 嗨 sas4eka,在初始化中使用特定值实现数组的时间并不让我感到惊讶。我报告了该值,因为它存在于我的测试中。为基于数组的 impl 添加是 O(1)?你说的是最好的情况吗?
    • 抱歉不准确。在这种情况下,我谈论的是 ArrayList add(Integer i) 方法,该方法最后添加一个元素并花费 O(1) 时间。 add(int index, Integer i) 方法,将一个元素放在 ArrayList 的中间,在最坏的情况下需要 O(n) 时间。
    • 我说的是for循环,你认为它的运行时间是多少?
    【解决方案3】:

    简而言之:对 ArrayList 的添加操作比对 LinkedList 更快,因为它涉及(读取、写入、更新)更少的内存位置:

    • ArrayList:更新int size字段,读取Object[] elements字段,读取elements.length,写入索引处的elements

    • LinkedList:更新int size字段,更新Entry tail字段,分配新的Entry,写入新条目的prevelement字段,写入tail.next字段。

    更精确的时间安排:

    @GenerateMicroBenchmark
    public int arrayList_add() {
        List<Object> list = new ArrayList<>(1000);
        Object x = new Object();
        for (int i = 0; i < 1000; i++) {
            list.add(x);
        }
        return list.size();
    }
    
    @GenerateMicroBenchmark
    public int linkedList_add() {
        List<Object> list = new LinkedList<>();
        Object x = new Object();
        for (int i = 0; i < 1000; i++) {
            list.add(x);
        }
        return list.size();
    

    结果:

    Benchmark             Mean   Mean error    Units
    arrayList_add        5,234        0,019  usec/op
    linkedList_add       6,417        0,032  usec/op
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-02
      相关资源
      最近更新 更多