1

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

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

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

考虑ArrayList<Integer> list如下:

列表

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

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

所以从这个角度来看,表现似乎差不多。如果从最后执行,是否还有其他原因可以使操作更快?

另一方面,对于LinkedList,倒数似乎为真;从头移除更快,而从尾移除需要几乎完全遍历,除非 atail-pointer 被保留。

4

4 回答 4

6

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

于 2013-03-12T02:10:50.737 回答
2

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

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

于 2013-03-12T02:11:24.730 回答
0

ArrayList<>开始删除很慢,因为自从添加函数后,整个数组将不得不移动add元素,如果我们将开始留空,那么我们将浪费内存。

如果我们考虑到大(O)符号从 start 删除本质上是线性时间O(n),而从 end 删除是恒定时间O(1)

于 2013-03-12T02:13:44.603 回答
0

虽然 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)and引起的混淆remove(Object o)

于 2018-03-01T10:48:01.130 回答