【问题标题】:java linkedlist slower than arraylist when adding elements?添加元素时,javalinkedlist比arraylist慢?
【发布时间】:2011-07-17 19:00:07
【问题描述】:

我认为在添加元素时,链表应该比数组列表更快?我刚刚测试了添加、排序和搜索元素(arraylist vs linkedlist vs hashset)需要多长时间。我只是将 java.util 类用于 arraylist 和linkedlist...使用每个类都可用的 add(object) 方法。

arraylist 在填充列表时执行了linkedlist...以及在列表的线性搜索中。

这是对的吗?是不是我在实现中做错了什么?

***************编辑******** *********

我只是想确保我正确使用了这些东西。这就是我正在做的事情:

public class LinkedListTest {

    private List<String> Names;

    public LinkedListTest(){
            Names = new LinkedList<String>();
    }

然后我只使用链表方法,即“Names.add(strings)”。当我测试数组列表时,它几乎是相同的:

public class ArrayListTest {

    private List<String> Names;

    public ArrayListTest(){
            Names = new ArrayList<String>();
    }

我做得对吗?

【问题讨论】:

  • 这取决于...您的测试代码是什么样的?如果您将 ArrayList 初始化为已知大小,则 ArrayList 可能会更快。 LinkedList 带来了一些额外的开销,因为每个元素都必须包装在一个节点中,以便它可以包含对 prev 和 next 的引用。 ArrayList 性能问题来自于在达到现有容量时初始化一个全新的支持数组并将所有元素复制到新数组。
  • @Mark - 不,没有将 arraylist 初始化为已知大小。

标签: java optimization arraylist linked-list


【解决方案1】:

是的,没错。 LinkedList 必须在每次插入时分配内存,而 ArrayList 被允许做更少的分配,给它amortized O(1) insertion。内存分配看起来很便宜,但实际上可能非常昂贵。

由于引用的局部性,LinkedList 中的线性搜索时间可能较慢:ArrayList 元素更靠近,因此cache misses 更少。

当您计划仅在 List 的末尾插入时,ArrayList 是首选实现。

【讨论】:

  • 好的,我想这是有道理的。我做了一些谷歌搜索......一些网站说添加到链表的末尾或开头应该比使用数组列表更快。为什么?我还阅读了一个网站,该网站说链表和数组列表的 add() 方法的时间复杂度为 O(1)。什么?真的吗?!?!
  • @user: ArrayList 的追加(在末尾插入)时间是 amortized O(1),这意味着它在执行 long 时平均为 O(1)系列的追加。它们中的任何一个都可能是 O(n),但那个容量会加倍,因此下一个 n 可以使用预先分配的空间。
  • @larsmans,缓存未命中是阵列备份结构的主要卖点。链接结构可能是每个节点上的缓存未命中,使得 O(n) 迭代实际上显着变慢。
  • 即使链表根本不包含任何缓存未命中,遍历也会变慢。这是因为访问每个项目都会产生两次取消引用操作的成本,而不是数组列表所需的单个操作。
  • 嗯,事实证明,数组是用一系列对象引用创建的,而对象本身驻留在内存中不相关的部分。如果你有一个对象数组而不是基元数组,它就像链表一样容易受到缓存未命中的影响。
【解决方案2】:

ArrayList 在访问随机索引数据时速度更快,但在列表中间插入元素时速度较慢,因为使用链表您只需要更改引用值。但是在数组列表中,您必须复制插入索引之后的所有元素,一个索引后面。

编辑:是否有一个链表实现可以记住最后一个元素?这样做可以加快使用链表在末尾插入的速度。

【讨论】:

  • 其实你也不能随机插入到LinkedList中,为了找到插入点需要遍历。
  • 保留对最后一个元素的引用会使追加从 O(n) 下降到 O(1),但在执行大量追加时仍然不会超过动态数组。
【解决方案3】:

当在 LinkedList 的后面添加一个元素时(在 Java 中 LinkedList 实际上是一个双向链表)它是一个O(1) 操作,就像在它的前面添加一个元素一样。在ith 位置添加元素大致是O(i) 操作。

因此,如果您要添加到列表的前面,LinkedList 会明显更快。

【讨论】:

  • Aren´t Java LinkedLists 在现实中不是双向链接的——我认为添加到哪一端并不重要。
  • 啊,我不知道实现细节,但那可能是真的。如有必要,我会调查并删除我的答案。
  • 看来LinkedList确实是一个双向链表,有意思。
  • @Argote - 那么这会使添加到链表的任一端比添加到数组列表更慢还是更快?
  • 嗯,在链表的开头添加应该更快,在数组列表的末尾添加应该稍微快一点。
【解决方案4】:

为什么你认为LinkedList 会更快?在一般情况下,插入数组列表只是更新单个数组单元的指针(使用 O(1) 随机访问)。 LinkedList 插入也是随机访问,但必须分配一个“单元”对象来保存条目,并更新一对指针,以及最终设置对被插入对象的引用。 p>

当然,ArrayList 的后备数组可能需要定期调整大小(如果选择的初始容量足够大,则不会出现这种情况),但由于数组呈指数增长,摊销成本会很低,并且以O(lg n) 复杂度为界。

简单地说 - 插入到数组列表中要简单得多,因此总体上要快得多。

【讨论】:

  • 好吧,如果我们将“插入”定义为在集合中的任意位置添加一个元素(这是默认定义),那么您必须在该位置之后移动所有元素。我会先提示一个有缺陷的基准..
【解决方案5】:

在这些情况下,链接列表可能比数组列表慢,原因有几个。如果您要插入到列表的末尾,则数组列表很可能已经分配了这个空间。底层数组通常以大块的形式增加,因为这是一个非常耗时的过程。所以,在大多数情况下,在后面添加一个元素只需要粘贴一个引用,而链表需要创建一个节点。在前面和中间添加应该会为两种类型的列表提供不同的性能。

列表的线性遍历在基于数组的列表中总是更快,因为它只能正常遍历数组。这需要每个单元格进行一次取消引用操作。在链表中,链表的节点也必须被解引用,耗时翻倍。

【讨论】:

    【解决方案6】:

    记住:

    • 对于给定数量的元素,“原始”性能以及不同结构缩放的方式存在差异;
    • 不同的结构在不同的操作中表现不同,这基本上是您在选择要使用的结构时需要考虑的一部分。

    因此,例如,链表在添加到末尾时有更多工作要做,因为它有一个额外的对象来分配和初始化每个添加的项目,但是无论每个项目的“内在”成本如何,两个结构都将有 O (1) 添加到列表末尾的性能,即无论列表的大小如何,每次添加都有一个有效的“恒定”时间,但 ArrayList 与 LinkedList 之间的常数会有所不同,并且后者可能更大。

    因此,在您的测试中,您可能需要同时考虑给定大小的操作的“原始”时间以及这些操作如何随着列表大小的增长而缩放

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-19
      • 2014-05-06
      • 2015-08-16
      • 1970-01-01
      • 2015-04-10
      • 1970-01-01
      相关资源
      最近更新 更多