我一直是一个简单使用的人:
List<String> names = new ArrayList<>();
我使用接口作为可移植性的类型名称,这样当我问类似这样的问题时,我就可以修改代码。
LinkedList何时应用于ArrayList,反之亦然?
我一直是一个简单使用的人:
List<String> names = new ArrayList<>();
我使用接口作为可移植性的类型名称,这样当我问类似这样的问题时,我就可以修改代码。
LinkedList何时应用于ArrayList,反之亦然?
当前回答
见原始答案下方作者的2021更新。
原答案(2011年)
作为一个在非常大规模的SOA web服务上做了大约十年操作性能工程的人,我更喜欢LinkedList而不是ArrayList的行为。虽然LinkedList的稳态吞吐量更差,因此可能会导致购买更多硬件,但ArrayList在压力下的行为可能会导致集群中的应用程序以近乎同步的方式扩展其阵列,而对于较大的阵列大小,可能会导致应用程序缺乏响应能力,在压力下停机,这是灾难性的行为。
类似地,您可以从默认的吞吐量固定垃圾收集器中获得更好的应用吞吐量,但一旦您获得了具有10GB堆的java应用程序,您就可以在完全GC期间锁定应用程序25秒,这会导致SOA应用程序超时和失败,如果太频繁,还会破坏SLA。尽管CMS收集器占用了更多的资源,并且没有实现相同的原始吞吐量,但它是一个更好的选择,因为它具有更可预测性和更小的延迟。
如果您所指的性能是吞吐量,并且可以忽略延迟,那么ArrayList只是性能的更好选择。根据我的工作经验,我不能忽视最坏情况下的延迟。
更新(2021 8月27日——10年后)
这个答案(也是我在SO问题上最受欢迎的答案)很可能是错误的(原因在下面的评论中概述)。我想补充一点,ArrayList将优化内存的顺序读取,并最小化缓存线和TLB未命中等。相比之下,当阵列增长超过边界时的复制开销可能无关紧要(可以通过高效的CPU操作完成)。考虑到硬件趋势,随着时间的推移,这个答案可能会变得更糟。LinkedList可能有意义的唯一情况是,如果您有数千个列表,其中任何一个都可能增长到GB大小,但在分配列表时无法做出正确的猜测,并且将它们全部设置为GB大小,则会炸毁堆。如果你发现了这样的问题,那么无论你的解决方案是什么,都需要重新设计(我不想轻率地建议重新设计旧代码,因为我自己维护了一堆又一堆的旧代码,但这是一个很好的例子,因为原始设计已经过时,确实需要扔掉)。尽管如此,我还是会把我几十年来的糟糕观点留在那里,让你读一读。简单、合乎逻辑,而且非常错误。
其他回答
请参阅Java教程-列表实现。
链接列表的一个重要特性(我在另一个答案中没有看到)是两个列表的串联。对于数组,这是O(n)(+某些重新分配的开销),对于链接列表,这只是O(1)或O(2);-)
重要提示:对于Java的LinkedList,这不是真的!请参阅Java中是否有一种快速的链表连接方法?
对于ArrayList和LinkedList,remove()和insert()的运行时效率都为O(n)。然而,线性处理时间背后的原因来自两个非常不同的原因:
在ArrayList中,您可以找到O(1)中的元素,但实际上删除或插入某些元素会使其成为O(n),因为以下所有元素都需要更改。
在LinkedList中,实际到达所需元素需要O(n),因为我们必须从一开始就开始,直到达到所需的索引。实际上,移除或插入是常量,因为我们只需要为remove()更改1个引用,为insert()更改2个引用。
插入和删除这两项中的哪一项更快取决于发生的位置。如果我们更接近开始,LinkedList将更快,因为我们必须经过相对较少的元素。如果我们接近末尾,ArrayList将更快,因为我们在恒定的时间内到达那里,只需更改紧随其后的几个剩余元素。如果正好在中间完成,LinkedList将更快速,因为遍历n个元素比移动n个值更快。
好处:虽然无法为ArrayList创建这两个方法O(1),但实际上在LinkedList中有一种方法可以做到这一点。假设我们想在整个列表中删除和插入元素。通常,您可以使用LinkedList从头开始每个元素,我们也可以使用迭代器“保存”当前正在处理的元素。在迭代器的帮助下,当在LinkedList中工作时,remove()和insert()的效率为O(1)。使其成为我所知的唯一性能优势,LinkedList总是优于ArrayList。
我已经阅读了答案,但有一种情况是,我总是使用LinkedList而不是ArrayList,我想分享它来听取意见:
每次我有一个方法返回从DB获得的数据列表时,我总是使用LinkedList。
我的理由是,因为不可能确切地知道我得到了多少结果,所以不会浪费内存(如ArrayList中的容量和实际元素数量之间的差异),也不会浪费时间复制容量。
至于ArrayList,我同意至少应该始终使用具有初始容量的构造函数,以尽可能减少数组的重复。
ArrayList本质上是一个数组。LinkedList实现为双链接列表。
答案很清楚。O(1)表示ArrayList,因为ArrayList允许使用索引进行随机访问。O(n)表示LinkedList,因为它需要首先查找索引。注意:添加和删除有不同的版本。
LinkedList在添加和删除时速度更快,但在获取时速度较慢。简而言之,在以下情况下,应首选LinkedList:
元素没有大量的随机访问有大量的添加/删除操作
==阵列列表===
添加(E E)在ArrayList末尾添加需要内存大小调整成本。O(n)最差,O(1)摊销add(int索引,E元素)添加到特定索引位置需要移动和可能的内存调整成本O(n)删除(int索引)删除指定的元素需要移动和可能的内存调整成本O(n)删除(对象o)从此列表中删除第一个出现的指定元素需要先搜索元素,然后移动&可能的内存调整成本O(n)
==链接列表===
添加(E E)添加到列表末尾O(1)add(int索引,E元素)在指定位置插入需要先找到位置O(n)删除()删除列表的第一个元素O(1)删除(int索引)删除具有指定索引的元素需要先找到元素O(n)删除(对象o)删除指定元素的第一个引用需要先找到元素O(n)
这是programcreek.com中的一个图(add和remove是第一种类型,即在列表末尾添加元素,然后在列表中的指定位置删除元素):