java集合中间插入_关于Java:在ArrayList 与LinkedList 中间插入

本问题已经有最佳答案,请猛点这里访问。

在Java的上下文中交谈。如果我想插入一个ArrayList或一个linkedList的中间,我被告知ArrayList会表现得很糟糕。

我理解这是因为,我们需要移动所有元素,然后进行插入。这应该是N/2的顺序,即O(N)。

但对于linkedList来说,情况不同吗?对于链表,我们需要遍历到找到中间位置,然后进行指针操作。在这种情况下,也需要O(N)时间。不是吗?

谢谢

可能更适合程序员stackexchange

任意插入对于ArrayList和LinkedList都是O(n)(对于平均和最坏的性能)。然后问题归结为哪个系数更大。分析并找出原因。

@达多-这里很好…依我所见

我不是说不能在这里回答,只是说它可能会引起程序员更多的注意。

这里的原因是链接列表中的元素没有实际的移动。链接列表是由节点组成的,每个节点都包含一个元素和一个指向下一个节点的指针。要将元素插入列表,只需要以下几点:

创建一个新节点来保存元素;

将前一个节点的下一个指针设置为新节点;

将新节点的下一个指针设置为列表中的下一个元素。

如果你曾经做过一系列回形针,你可以把每一个回形针看作是回形针链的开始,以及其后的所有回形针。要将新的回形针插入链条,只需在新回形针将要插入的位置断开回形针,然后插入新回形针。LinkedList就像一个回形针链。

ArrayList类似于一个柱箱或一个Mancala板,每个隔间只能容纳一个物品。如果你想在中间插入一个新的元素(并且保持所有元素的顺序相同),你就必须将所有元素都移到该位置之后。

在链表中给定节点之后的插入是固定时间,只要您已经对该节点引用(在Java中使用EDCOX1×2),到达该位置通常需要在节点的位置上线性时间。也就是说,要到达第n个节点,需要采取n个步骤。在数组列表(或数组,或任何基于连续内存的结构)中,列表中第个元素的地址只是(第一个元素的地址)+n×(元素的大小),一个小的算术位,我们的计算设备支持对任意内存地址的快速访问。

而ArrayList试图总是有大约10个空元素来处理扩展,因此通过插入中间,它将数组克隆到元素重新排序之上的另一个数组。

@Richardtingle就是我要说的,它必须对元素重新排序,并且在使用ArrayList的时候克隆它。因此,对于这个"简单"的任务,会发生更多的操作。

@JoshuaTaylor但是为了这个你需要遍历列表直到插入点。是shifting过程代价高昂吗?如果是,你能告诉我为什么是这样吗?否则,即使LinkedList也会采取n/2步骤到达列表中间进行插入。

@Kraken你忘了每次你添加元素到一个ArrayList,它必须创建一个全新的ArrayList,并克隆所有元素。它通过迭代内部化的数组来实现这一点。因此,这比简单地更改两个引用(比如使用某种类型的链接列表)要复杂得多。

@克雷肯啊,是的,继续用回形针做类比;如果你还没有把第n个回形针放在链子里,它需要采取步骤才能找到它。但是,如果您已经用一个ListIterator来保存它,那么实际插入的时间是恒定的。

LinkedList中的任意插入是o(n),就像ArrayList中的插入一样。对于LinkedList,边迭代边插入速度更快。

@snakedoc但是arraylist有一些额外的空间用于相同的目的。所以尽管我们有可能达到满负荷,但我所说的另一种情况是,当我们有足够的空间添加而不重新分配时

@Kraken你不能决定何时ArrayList重新分配它的内部化数组…它只是自动发生的。我可能是错的,但我认为它总是争取有10个自由元素,这意味着如果你添加一个元素,它可能会或可能不会重新分配。所以基本上你必须假设它会在每次添加时重新分配。

@Tedhop这是一种常见的描述方法,但我真的希望更多的教员/教科书等能够描述实际的复合操作。给定一个链接列表中的节点,添加一个新节点作为其下一个节点的时间是恒定的。检索给定节点是O(N)。给定一个数组,将元素写入数组中的某个位置是恒定的时间。在数组中调整n个元素是O(N),这样就可以打开一个特定的位置进行写入。

@snakedoc您可以预先分配数组中的空间,并决定何时这样做(使用ensureCapacity)。另外,我正在研究的JDK在需要重新分配时将由1.5 times增长。

遍历到LinkedList中的节点与移动以打开ArrayList中的槽的相对性能完全不清楚。LinkedList中的节点可以广泛分散在内存中,遍历到一个节点可能涉及多个缓存未命中,而ArrayList具有引用位置的优势。另外,由于ArrayList通常具有额外的容量,因此并不总是需要重新分配内部数组。在这两种情况下,都应该研究插入物的摊余成本。实际性能取决于系统负载、LinkedList在内存中的分布方式等。

@泰德霍普,我同意你所说的一切。我的观点很简单,因为LinkedList支持基于ListIterator的固定时间插入,因此ArrayList没有对应的插入。如果已知将使用LinkedList,则可以出于性能原因利用它。重复"在链表中插入是O(N)"并不意味着,对于链表的许多用途,插入实际上是一个固定时间的操作。

@泰德霍普,这实际上是在最近的一个答案中,一个操作想要交错两个列表(长度m和n)。仅使用list.add的解决方案是o(m n),但使用listirator.add的版本(只要列表是LinkedList)是o(m+n)。简单地说,在链接列表中插入需要O(N)不会留下空间,因为您可以使用m+n操作而不是m n操作在长度为n的列表中插入m元素。

我同意在许多操作中,LinkedList比ArrayList有明显的优势。但是,合并列表的场景(包括在遍历过程中插入)与OP的问题(即"中间插入")无关。

我认为,在分析复杂性时,您需要考虑到您使用的度量标准。在ArrayList中,您的度量是shuffling,这只是赋值。但这是一个相当复杂的操作。

另一方面,您使用的是LinkedList,您只需查看参考。实际上,您只执行1次插入。因此,虽然算法的复杂性最终会相似,但在O(n)时间执行的实际进程是不同的。在ArrayList的情况下,它执行了大量的内存操作。在一个LinkedList的例子中,它只是一个读数。

对于那些说他不了解LinkedList的人

LinkedList只在开始处有一个指针,在结束处有一个指针。它不会自动知道要删除的节点后面的节点(除非它是双链接列表),所以您需要遍历该列表,从创建临时指针开始,直到到达要删除的节点之前的节点,我相信OP正在讨论这个问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值