【问题标题】:Removing elements from the beginning vs the end从头到尾删除元素
【发布时间】:2013-03-12 02:07:07
【问题描述】:

List interface 的 Collection 框架教程中,有一句关于从 List 实现中删除元素的性能的有趣引用:

对于许多常见的 List 实现,例如 ArrayList,从列表末尾移除元素的性能大大优于从开头移除元素。

教程没有进一步解释这一点,我试图准确理解为什么会这样。

考虑如下ArrayList<Integer> list

在第一种情况下,如果我们从 list 的末尾删除最后 4 个元素,那么这些值将设置为 null(或等效项)。我的理论是,当需要进行复制操作时,只会复制不是null 的元素。

在第二种情况下,如果我们删除前 4 个元素,它们将被设置为null,并且只会复制非null 元素。

所以从这个角度来看,表演似乎大致相同。如果从头开始执行,是否还有其他原因导致操作更快?

另一方面,对于LinkedList,情况正好相反;从头删除更快,而从末尾删除需要几乎完全遍历,除非tail-pointer 保留。

【问题讨论】:

    标签: java performance list


    【解决方案1】:

    据我了解, ArrayList 是列表的数组实现。因此,如果从数组的开头删除元素,则需要移动所有剩余元素以填充已删除的元素。所以这本质上将是一个 O(n-1) 操作。但是,当您从列表末尾删除元素时,情况并非如此。这将是 O(1)。

    【讨论】:

      【解决方案2】:

      从列表的开头删除元素不仅仅是将元素设置为null。您还需要移动剩余的元素以填充空出的位置。

      可以使用变量来跟踪列表的“开始”而不移动元素,但是由于数组中未使用的元素,您将牺牲内存效率。

      【讨论】:

      • 谢谢,我只是在想,每次删除一个元素时进行一次移位非常昂贵,但是,我猜这样做是为了保持数组一致并且与非null(或已删除)值。
      • @arin “我只是在想,每次删除一个元素时进行一次转换非常昂贵” 没错!替代方案也是如此。
      【解决方案3】:

      ArrayList<> 中,从开头删除很慢,因为自从添加函数add 之后,整个数组都必须移动,如果我们将开头留空,那么我们将浪费内存。

      如果我们考虑到 big(O) 表示法,从开始删除基本上是线性时间 O(n),而从结束删除是常数时间 O(1)

      【讨论】:

        【解决方案4】:

        虽然 Code-Apprentice 的回答是正确的,但我想详细说明如何通过基于索引的查找来验证每个条目向左移动的行为,即调用 list.get(index)

        1. 添加两个条目
        2. 删除第一个元素
        3. 检查第一个元素的值:现在是 null 还是之前的第二个元素?

        要验证的代码:

        List<String> list = new ArrayList<>();
        list.add("0");
        list.add("1");
        list.remove(0);
        String entry0 = list.get(0);
        System.out.println(entry0);
        

        您怀疑null 会被打印,但打印的是“1”。 这确认所有元素都向左移动了。

        注意。我使用字符串来避免remove(int index)remove(Object o) 引起的混淆。

        【讨论】:

          猜你喜欢
          • 2016-04-30
          • 1970-01-01
          • 2014-11-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-03-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多