在 ArrayList 与 LinkedList 中间插入 [重复]
Posted
技术标签:
【中文标题】在 ArrayList 与 LinkedList 中间插入 [重复]【英文标题】:Insertion in the middle of ArrayList vs LinkedList [duplicate] 【发布时间】:2013-10-17 01:04:40 【问题描述】:在 Java 的上下文中交谈。如果我想在ArrayList
或linkedList
的中间插入,我被告知Arraylist
的性能会非常糟糕。
我知道这是因为,我们需要移动所有元素然后进行插入。这应该是 n/2 的顺序,即 O(n)。
但linkedList
不一样。对于链表,我们需要遍历直到找到中间,然后进行指针操作。在这种情况下,也需要 O(n) 时间。不会吧?
谢谢
【问题讨论】:
可能更适合程序员 stackexchangeArrayList
和 LinkedList
的任意插入都是 O(n)(对于平均和最坏情况的性能)。然后问题归结为哪个系数更大。分析并找出答案。
@dardo - 这里很好...... IMO
我不是说这里不能回答,只是说它可能会得到程序员更多的关注。
【参考方案1】:
这里的原因是链表中的元素没有真正的移动。链表由节点构成,每个节点都包含一个元素和指向下一个节点的指针。将元素插入列表只需要几件事:
-
创建一个新节点来保存元素;
将前一个节点的next指针设置为新节点;
将新节点的next指针设置为列表中的下一个元素。
如果您曾经制作过回形针链,您可以将每个回形针视为它链的开始,以及它之后的所有回形针。要将新的回形针插入链条中,您只需在新回形针所在的位置断开回形针,然后插入新回形针即可。 LinkedList
就像一个回形针链。
ArrayList
有点像pillbox 或mancala board,其中每个隔间只能容纳一件物品。如果你想在中间插入一个新元素(并使所有元素保持相同的顺序),你将不得不在那个位置之后移动 everything。
在链表中给定节点之后的插入是恒定时间,只要您已经拥有对该节点的引用(在 Java 中使用 ListIterator
),并到达该位置通常需要与节点位置成线性关系的时间。也就是说,到达第_n_个节点需要n个步骤。在数组列表(或数组,或任何基于连续内存的结构,实际上)中,列表中第_n_个元素的地址只是(第一个元素的地址)+n×(元素的大小),一个微不足道的算术,我们的计算设备支持快速访问任意内存地址。
【讨论】:
和ArrayList
试图总是有大约 10 个空元素来处理扩展,因此通过在中间插入,它将数组克隆到元素重新排序之上的另一个数组。跨度>
@RichardTingle 这就是我要提出的观点,它必须重新排序元素,并在使用ArrayList
时克隆它。所以这个“简单”任务会发生更多的操作。
@Joshua Taylor 但是为此,无论如何您都需要遍历列表直到插入点。 shifting
过程成本高吗?如果是这样,你能告诉我为什么会这样吗?因为否则,即使是 LinkedList 也需要 n/2 步才能到达列表的中间进行插入。
@Kraken 您忘记了每次向ArrayList
添加元素时,它都必须创建一个全新的ArrayList
并克隆所有元素。它通过迭代内部化数组来做到这一点。因此,这比使用某种链接列表简单地更改几个引用要多得多。
@SnakeDoc 您可以在数组中预先分配空间并决定何时这样做(使用ensureCapacity
)。此外,当需要重新分配时,我正在查看的 JDK 将增长 1.5 times
。【参考方案2】:
我认为,在分析复杂性时,您需要考虑您正在使用的 metric。在ArrayList
中,您的指标是shuffling
,这只是赋值。但这是一个相当复杂的操作。
另一方面,您使用的是LinkedList
,而您只是在寻找参考。实际上,您只执行 1 次插入。因此,虽然算法复杂性最终会相似,但在O(n)
时间执行的实际过程是不同的。对于ArrayList
,它正在执行大量内存操作。对于LinkedList
,它只是在读取。
对于那些说他不懂 LinkedLists 的人
LinkedList 只有一个指向开头和一个指针在结尾。它不会自动知道要删除的节点后面的节点(除非它是双向链表),因此您需要遍历列表,从一开始就创建一个临时指针,直到您来到您要删除的节点之前的节点,我相信这是OP正在讨论的。
【讨论】:
以上是关于在 ArrayList 与 LinkedList 中间插入 [重复]的主要内容,如果未能解决你的问题,请参考以下文章
Java中arraylist和linkedlist源码分析与性能比较