我一直是一个简单使用的人:
List<String> names = new ArrayList<>();
我使用接口作为可移植性的类型名称,这样当我问类似这样的问题时,我就可以修改代码。
LinkedList何时应用于ArrayList,反之亦然?
我一直是一个简单使用的人:
List<String> names = new ArrayList<>();
我使用接口作为可移植性的类型名称,这样当我问类似这样的问题时,我就可以修改代码。
LinkedList何时应用于ArrayList,反之亦然?
当前回答
以下是ArrayList和LinkedList以及CopyOnWrite ArrayList中的Big-O符号:
阵列列表
get O(1)
add O(1)
contains O(n)
next O(1)
remove O(n)
iterator.remove O(n)
链表
get O(n)
add O(1)
contains O(n)
next O(1)
remove O(1)
iterator.remove O(1)
CopyOnWrite阵列列表
get O(1)
add O(n)
contains O(n)
next O(1)
remove O(n)
iterator.remove O(n)
基于这些,您必须决定选择什么。:)
其他回答
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是第一种类型,即在列表末尾添加元素,然后在列表中的指定位置删除元素):
以下是ArrayList和LinkedList以及CopyOnWrite ArrayList中的Big-O符号:
阵列列表
get O(1)
add O(1)
contains O(n)
next O(1)
remove O(n)
iterator.remove O(n)
链表
get O(n)
add O(1)
contains O(n)
next O(1)
remove O(1)
iterator.remove O(1)
CopyOnWrite阵列列表
get O(1)
add O(n)
contains O(n)
next O(1)
remove O(n)
iterator.remove O(n)
基于这些,您必须决定选择什么。:)
与LinkedList相比,Summary ArrayList和ArrayDeque在更多的用例中更可取。如果您不确定,请从ArrayList开始。
TLDR,在ArrayList中,访问元素需要恒定的时间[O(1)],添加元素需要O(n)时间[最坏情况]。在LinkedList中,插入元素需要O(n)时间,访问也需要O(n)时间,但LinkedList比ArrayList使用更多内存。
LinkedList和ArrayList是List接口的两种不同实现。LinkedList使用双链接列表实现它。ArrayList通过动态调整数组大小来实现它。
与标准的链表和数组操作一样,不同的方法将有不同的算法运行时。
对于LinkedList<E>
get(int index)为O(n)(平均步数为n/4),但当index=0或index=list.size()-1时为O(1)(在这种情况下,还可以使用getFirst()和getLast())。LinkedList的主要优点之一add(int index,E元素)为O(n)(平均步数为n/4),但当index=0或index=list.size()-1时为O(1)(在这种情况下,还可以使用addFirst()和addLast()/add())。LinkedList的主要优点之一remove(int index)为O(n)(平均步数为n/4),但当index=0或index=list.size()-1时为O(1)(在这种情况下,还可以使用removeFirst()和removeLast())。LinkedList的主要优点之一Iterator.remove()为O(1)。LinkedList的主要优点之一ListIterator.add(E元素)为O(1)。LinkedList的主要优点之一
注:许多操作平均需要n/4步,在最佳情况下(例如索引=0)需要恒定的步数,在最坏情况下(列表中间)需要n/2步
对于ArrayList<E>
get(int索引)为O(1)。ArrayList的主要优势<E>add(E元素)是O(1)摊销,但O(n)最坏情况,因为数组必须调整大小并复制add(int索引,E元素)为O(n)(平均n/2步)remove(int索引)为O(n)(平均n/2步)Iterator.remove()为O(n)(平均为n/2步)ListIterator.add(E元素)为O(n)(平均n/2步)
注:许多操作平均需要n/2步,在最佳情况下(列表末尾)需要恒定的步数,在最坏情况下(开始列表)需要n步
LinkedList<E>允许使用迭代器进行恒定时间的插入或删除,但只能对元素进行顺序访问。换句话说,您可以向前或向后遍历列表,但在列表中找到位置所需的时间与列表的大小成正比。Javadoc表示“索引到列表中的操作将从开始或结束遍历列表,以较近者为准”,因此这些方法平均为O(n)(n/4步),尽管索引=0时为O(1)。
另一方面,ArrayList<E>允许快速随机读取访问,因此您可以在恒定时间内获取任何元素。但是,除了末端之外,任何地方的添加或删除都需要将后面的所有元素转换过来,要么打开,要么填补空白。此外,如果添加的元素超过了基础数组的容量,则会分配一个新数组(大小的1.5倍),并将旧数组复制到新数组,因此在最坏的情况下,添加到ArrayList是O(n),但平均来说是常量。
因此,根据您打算执行的操作,您应该相应地选择实现。对这两种列表进行迭代实际上都是同样便宜的。(在ArrayList上迭代在技术上更快,但除非您正在做一些对性能非常敏感的事情,否则不必担心这一点——它们都是常量。)
使用LinkedList的主要好处是重用现有迭代器来插入和删除元素。然后,这些操作可以在O(1)中通过仅本地更改列表来完成。在阵列列表中,需要移动(即复制)阵列的其余部分。另一方面,在LinkedList中查找意味着在最坏情况下遵循O(n)(n/2步)中的链接,而在ArrayList中,所需位置可以通过数学计算并在O(1)中访问。
使用LinkedList的另一个好处是在列表的开头添加或删除,因为这些操作是O(1),而ArrayList是O(n)。请注意,ArrayDeque可能是LinkedList的一个很好的替代方案,用于添加和删除头部,但它不是List。
此外,如果您有大量列表,请记住内存使用情况也不同。LinkedList的每个元素都有更多的开销,因为指向下一个和上一个元素的指针也会被存储。ArrayList没有这个开销。然而,ArrayList占用的内存与为容量分配的内存一样多,而不管是否实际添加了元素。
ArrayList的默认初始容量非常小(Java 1.4-1.8中为10)。但由于底层实现是一个数组,如果添加大量元素,则必须调整数组的大小。为了避免在知道要添加大量元素时调整大小的高昂成本,请使用更高的初始容量构建ArrayList。
如果使用数据结构透视图来理解这两个结构,LinkedList基本上是一个包含头节点的顺序数据结构。Node是两个组件的包装器:一个类型为T的值[通过泛型接受],另一个对链接到它的Node的引用。因此,我们可以断言它是一个递归数据结构(一个Node包含另一个节点,该节点具有另一个Node等等…)。如上所述,在LinkedList中添加元素需要线性时间。
ArrayList是一个可增长的数组。它就像一个常规数组。在后台,当添加了一个元素,并且ArrayList已经满了容量时,它将创建另一个大小大于先前大小的数组。然后将元素从先前的数组复制到新的数组,并且将要添加的元素也放置在指定的索引处。
ArrayList和LinkedList都实现了List接口,它们的方法和结果几乎相同。然而,它们之间几乎没有区别,这取决于需求,使一个优于另一个。
阵列列表与链接列表
1) 搜索:与LinkedList搜索操作相比,ArrayList搜索操作非常快。ArrayList中的get(int index)给出了O(1)的性能,而LinkedList的性能为O(n)。
原因:ArrayList为其元素维护基于索引的系统,因为它隐式使用数组数据结构,这使得在列表中搜索元素的速度更快。另一方面,LinkedList实现了双链接列表,这需要遍历所有元素来搜索元素。
2) 删除:LinkedList删除操作提供O(1)性能,而ArrayList提供可变性能:最坏情况下(删除第一个元素时)为O(n),最好情况下(移除最后一个元素时,为O(2)。
结论:LinkedList元素删除速度比阵列列表。
原因:LinkedList的每个元素都有两个指针(地址),指向列表中的两个相邻元素。因此,移除仅需要改变将要移除的节点的两个相邻节点(元素)中的指针位置。当在ArrayList中时,需要移动所有元素以填充移除的元素所创建的空间。
3) 插入性能:LinkedList add方法提供O(1)性能,而ArrayList在最坏情况下提供O(n)性能。原因与删除说明相同。
4) 内存开销:ArrayList维护索引和元素数据,而LinkedList维护相邻节点的元素数据和两个指针
因此LinkedList中的内存消耗相对较高。
这些类之间几乎没有相似之处,如下所示:
ArrayList和LinkedList都是List接口的实现。它们都保持元素插入顺序,这意味着在显示ArrayList和LinkedList元素时,结果集将具有元素插入列表的相同顺序。这两个类都是非同步的,可以使用Collections.synchronizedList方法显式同步。这些类返回的迭代器和listIterator是快速失败的(如果在创建迭代器之后的任何时候对列表进行结构修改,除了通过迭代器自己的remove或add方法之外,其他任何方式,迭代器都会抛出ConcurrentModificationException)。
何时使用LinkedList,何时使用ArrayList?
如上所述,与ArrayList(O(n))相比,插入和删除操作在LinkedList中提供了良好的性能(O(1))。因此,若应用程序中需要频繁添加和删除,则LinkedList是最佳选择。搜索(get方法)操作在Arraylist(O(1))中很快,但在LinkedList(O(n))中不快因此,如果添加和删除操作更少,搜索操作需求更多,ArrayList将是您的最佳选择。
我的经验法则是,如果我需要一个集合(即不需要是一个列表),那么如果你事先知道大小,或者可以自信地知道大小,或知道它不会有太大变化,那么就使用ArrayList。如果您需要随机访问(即使用get(index)),请避免LinkedList。基本上,只有当您不需要索引访问并且不知道正在分配的集合的(近似)大小时,才使用LinkedList。此外,如果您要进行大量添加和删除(再次通过Collection接口),则LinkedList可能更可取。