LinkedList和ArrayList是List接口的两种不同实现。LinkedList使用双向链表实现它。ArrayList使用动态重新调整数组的大小来实现它。
与标准链表和数组操作一样,各种方法将具有不同的算法运行时。
对于 LinkedList<E>
1.get(int index)是O(n)(平均n / 4步)
2.add(E element)是O(1)
3.add(int index, E element)是O(n)(平均n / 4步),但O(1)时index = 0 <---LinkedList<E>的主要好处
4.remove(int index)是O(n)(平均n / 4步)
5.Iterator.remove()是O(1)。<---LinkedList<E>的主要好处
6.ListIterator.add(E element)是O(1)<-- LinkedList<E> 这是主要的好处之一
注意:许多操作平均需要n / 4步,最佳情况下需要恒定步数(例如索引= 0),最坏情况下需要n / 2步(列表中间)
对于 ArrayList<E>
1.get(int index)是O(1) <---ArrayList<E>主要的好处
2.add(E element)是O(1)摊销,但O(n)最坏情况,因为数组必须调整大小并复制
3.add(int index, E element)是O(n)(平均n / 2步)
4.remove(int index)是O(n)(平均n / 2步)
5.Iterator.remove()是O(n)(平均n / 2步)
6.ListIterator.add(E element)是O(n)(平均n / 2步)
注意:许多操作平均需要n / 2步,最好的情况下是常数步(列表末尾),最坏情况下是n步(列表开头)
LinkedList<E>允许使用迭代器进行常量插入或删除,但只允许顺序访问元素。换句话说,您可以向前或向后遍历列表,但在列表中查找位置需要的时间与列表的大小成比例。Javadoc说“index索引到列表中的操作将从开头或结尾遍历列表,以较近者为准”,因此这些方法平均为O(n)(n / 4步),但O(1)为index = 0。
ArrayList<E>另一方面,允许快速随机读取访问,因此您可以在恒定时间内获取任何元素。但是,除了末端之外的任何地方添加或移除都需要将所有后面的元素移位,以便打开或填补空白。此外,如果您比下面阵列的容量添加更多的元件,一个新的数组(1.5倍的尺寸)被分配,而旧的阵列被复制到新的一个,因此添加到ArrayList是O(n)的在最坏的情况但平均不变。
因此,根据您打算执行的操作,您应该相应地选择实现。迭代任何一种List实际上同样成本。(迭代一个ArrayList从技术上更快,但除非你做的事情对性能非常敏感,否则你不应该担心这一点 - 它们都是常量。)
使用LinkedList的主要好处是当您重复使用现有迭代器来插入和删除元素时,因为这些操作的时间复制度是O(1)完成这些操作。在数组列表中,需要移动(即复制)数组的其余部分。另一方面,在最坏情况下LinkedList在O(n)(n / 2步)中的链接之后寻找,而在ArrayList期望的位置可以在数学上计算并在O(1)中访问。
使用的另一个好处LinkedList出现当您添加或从列表中的头去掉,因为这些操作是O(1) ,而ArrayList则是为O(n)。请注意,这ArrayDeque可能是LinkedList添加和删除头部的一个很好的替代方案,但它不是一个List。
此外,如果您有大型列表,请记住内存使用情况也不同。一个LinkedList的每个元素都有更多的开销,因为还存储了指向下一个和前一个元素的指针。ArrayLists没有这个开销。但是,ArrayLists无论是否实际添加了元素,都会占用为容量分配的内存。
一个默认初始容量ArrayList非常小(Java 1.4中的10 - 1.8)。但由于底层实现是一个数组,因此如果添加大量元素,则必须调整数组大小。当您知道要添加大量元素时,为了避免调整大小的高成本,请构建ArrayList具有更高初始容量的内容
综上所述;
链表优于数组:
a)您需要从列表中进行恒定时间插入/删除(例如在时间可预测性非常关键的实时计算中)
b)您不知道列表中有多少项。对于数组,如果数组变得太大,您可能需要重新声明和复制内存
c)您不需要随机访问任何元素
d)您希望能够在列表中间插入项目(例如优先级队列)
数组优于链表:
a)您需要索引/随机访问元素
b)提前了解数组中元素的数量,以便为数组分配正确的内存量
c)在按顺序迭代所有元素时需要速度。您可以在数组上使用指针数学来访问每个元素,而您需要根据链表中每个元素的指针查找节点,这可能会导致页面错误,从而导致性能下降。
d)记忆是一个问题。填充数组占用的内存少于链表。数组中的每个元素都只是数据。每个链表节点都需要数据以及指向链表中其他元素的一个(或多个)指针。