何时在Java中通过ArrayList使用LinkedList?


3122

我一直是一个简单使用的人:

List<String> names = new ArrayList<>();

我将接口用作可移植性的类型名称,这样当我问诸如此类的问题时,便可以重新编写代码。

什么时候应该LinkedList使用过ArrayList,反之亦然?



1
只要看一下LinkedList作者stackoverflow.com/a/42529652/2032701的引用,您就会对问题有实际的认识。
Ruslan

Answers:


3371

摘要 ArrayListArrayDeque在最好许多更多使用情况比LinkedList。如果不确定,请从开始ArrayList


LinkedList并且ArrayList是List接口的两种不同的实现。LinkedList用双向链表实现它。ArrayList用动态调整大小的数组实现它。

与标准的链表和数组操作一样,各种方法将具有不同的算法运行时。

对于 LinkedList<E>

  • get(int index)O(n)(平均有n / 4步),但在或时为O(1)(在这种情况下,您也可以使用和)。的主要好处之一index = 0index = list.size() - 1getFirst()getLast() LinkedList<E>
  • add(int index, E element)O(n)(平均n / 4步),但在或时为O(1)(在这种情况下,您也可以使用和/ )。的主要好处之一index = 0index = list.size() - 1addFirst()addLast()add() LinkedList<E>
  • remove(int index)O(n)(平均有n / 4步),但在或时为O(1)(在这种情况下,您也可以使用和)。的主要好处之一index = 0index = list.size() - 1removeFirst()removeLast() LinkedList<E>
  • Iterator.remove()O(1)的主要好处之一 LinkedList<E>
  • ListIterator.add(E element)O(1)的主要好处之一 LinkedList<E>

注意:许多操作平均需要n / 4步,在最佳情况下(例如,索引= 0)需要恒定的步数,在最坏情况下(列表的中间)需要n / 2

对于 ArrayList<E>

  • get(int index)O(1)主要优点 ArrayList<E>
  • add(E element)被分摊为O(1),但最差情况为O(n),因为必须调整数组大小并复制该数组
  • add(int index, E element)O(n)(平均有n / 2步)
  • remove(int index)O(n)(平均有n / 2步)
  • Iterator.remove()O(n)(平均有n / 2步)
  • ListIterator.add(E element)O(n)(平均有n / 2步)

注意:许多操作平均需要n / 2步,在最佳情况下(列表结束)需要恒定的步数,在最坏情况下(列表开始)需要n

LinkedList<E>允许使用迭代器进行固定时间的插入或删除,但只能顺序访问元素。换句话说,您可以向前或向后浏览列表,但是在列表中查找位置所花费的时间与列表的大小成正比。Javadoc说:“索引到列表中的操作将从开头或结尾遍历列表,以较近者为准”,因此,这些方法的平均值为O(n)n / 4步),尽管O(1)index = 0

ArrayList<E>另一方面,允许快速随机读取访问,因此您可以在固定时间内抓取任何元素。但是从末端开始的任何地方添加或删除,都需要将后面的所有元素移开,以形成开口或填补空白。同样,如果添加的元素多于基础数组的容量,则会分配一个新数组(大小为1.5倍),并将旧数组复制到新数组中,因此在最坏的情况下将a添加ArrayListO(n)情况,但平均保持不变。

因此,根据您打算执行的操作,您应该相应地选择实现。遍历任何一种List实际上都是很便宜的。(从ArrayList技术上讲,迭代速度更快,但除非您确实对性能敏感,否则您不必为此担心-它们都是常量。)

LinkedList当您重复使用现有迭代器来插入和删除元素时,使用generic 的主要好处。然后可以通过仅在本地更改列表在O(1)中完成这些操作。在阵列列表中,阵列的其余部分需要移动(即复制)。另一方面,在最差的情况下LinkedListO(n)n / 2步)的链接寻找均值,而在ArrayList所需位置可以数学计算并在O(1)中访问。

使用的另一个好处LinkedList出现当您从列表的头部添加或删除,因为这些操作是O(1) ,而他们是为O(n)进行ArrayList。请注意,这ArrayDeque可能是LinkedList添加和删​​除头部的不错选择,但不是List

另外,如果列表很大,请记住,内存使用情况也有所不同。a的每个元素LinkedList都有更多开销,因为还存储了指向下一个和上一个元素的指针。ArrayLists没有这个开销。但是,ArrayLists无论是否实际添加了元素,占用的内存都应为该容量分配的内存。

的默认初始容量ArrayList很小(Java 1.4-1.8中为10)。但是由于底层实现是一个数组,因此如果添加很多元素,则必须调整数组的大小。为了避免在确定要添加很多元素时调整大小的高成本,请ArrayList以较高的初始容量构造。


182
忘了提及插入费用。在LinkedList中,一旦位置正确,插入成本为O(1),而在ArrayList中,插入成本为O(n)-超出插入点的所有元素都必须移动。
大卫·罗德里格斯(DavidRodríguez)-德里贝斯

26
关于Vector的使用:确实没有必要回到Vector。完成此操作的方法是使用首选的List实现和对syncedList的调用以为其提供同步包装器。请参阅:java.sun.com/docs/books/tutorial/collections/implementations/...
瑞安考克斯

69
不,对于LinkedList,即使您知道该位置,get仍为O(n),因为为了到达该位置,基础实现必须遍历链表的“下一个”指针以获取该位置的值。没有随机访问之类的东西。对于位置2,步行指针可能很便宜,但是对于位置1百万,并不是那么便宜。关键是,它与位置成正比,这意味着它是O(n)。
乔纳森·特兰

53
@Kevin内存“靠在一起”可能很重要。硬件会将连续的内存块(动态RAM)缓存到L1或L2缓存中更快的静态RAM中。从理论上并且在大多数情况下,实际上,可以将内存视为随机访问。但实际上,顺序读取内存要比随机顺序读取稍快。对于性能至关重要的循环,这可能很重要。他们称其为“空间局部性”或参考局部性
乔纳森·陈

92
没有O(n/2)或的东西O(n/4)。大号O表示您如何用更大的n 缩放操作。需要逐步执行的操作与需要逐步执行的操作完全一样地按比例缩放,这就是删除常量求幂或因数的原因。而且都是正义的。并且具有不同的常数因子,因此将a中的a 与另一个中的a 进行比较就没有意义,两者都只表示线性缩放操作。n/2nO(n/2)O(n/4)O(n)LinkedListArrayListO(n/2)O(n/4)
Holger

630

到目前为止,除了普遍认为a LinkedList比a “多得多”之外,似乎没有人解决过这些列表的内存占用问题,ArrayList因此我进行了一些数字运算来证明这两个列表占用了N个空引用的确切数量。

由于参考在它们的相对系统上是32位还是64位(即使为null),因此我包括了32和64位LinkedListsand的4组数据ArrayLists

注意:ArrayList行显示的大小适用于修剪后的列表 -实际上,中的后备阵列的容量ArrayList通常大于其当前元素数。

注意2 :( 感谢BeeOnRope)由于CompressedOops现在是JDK6或更高版本的默认值,因此下面的64位计算机的值将基本上与32位计算机匹配,除非您明确地将其关闭。


LinkedList和ArrayList的图元素数x字节


结果清楚地表明 LinkedList远远超过ArrayList,尤其是元素数量很高时。如果内存是一个因素,请避免使用LinkedLists

我遵循的公式如下,如果我做错了什么,请告诉我,我将对其进行修复。对于32或64位系统,“ b”为4或8,而“ n”为元素数。请注意,使用mod的原因是因为java中的所有对象将占用8字节空间的倍数,而不管其是否全部使用。

数组列表:

ArrayList object header + size integer + modCount integer + array reference + (array oject header + b * n) + MOD(array oject, 8) + MOD(ArrayList object, 8) == 8 + 4 + 4 + b + (12 + b * n) + MOD(12 + b * n, 8) + MOD(8 + 4 + 4 + b + (12 + b * n) + MOD(12 + b * n, 8), 8)

链表:

LinkedList object header + size integer + modCount integer + reference to header + reference to footer + (node object overhead + reference to previous element + reference to next element + reference to element) * n) + MOD(node object, 8) * n + MOD(LinkedList object, 8) == 8 + 4 + 4 + 2 * b + (8 + 3 * b) * n + MOD(8 + 3 * b, 8) * n + MOD(8 + 4 + 4 + 2 * b + (8 + 3 * b) * n + MOD(8 + 3 * b, 8) * n, 8)


2
非常有趣的是,LinkedList与ArrayList一样需要大量内存来存储单个元素。多么不直观!如果使用-XX:+ UseCompressedOops运行示例,会发生什么情况?
jontejj 2013年

215
数学的问题在于,图形会极大地放大影响。您正在建模的对象仅包含一个int,即4或8个字节的数据。在链表中,开销实际上有4个“单词”。因此,您的图形给人的印象是,链表使用“五次”存储阵列列表。错了 每个对象的开销为16或32字节,作为累加调整,而不是缩放因子。
Heath Hunnicutt 2013年

6
没有ArrayList / LinkedList / Node对象仅包含一个int,所以我听不到您在说什么。我已经将“对象开销”改写为“对象标头”以明确说明-每个对象都有一个8字节的标头,而不管系统如何,是的,它包括LinkedList中的所有Node对象,据我所知,所有这些对象均正确计数告诉。顺便说一句,在再次查看时,我在LinkedList中发现了数学上的其他一些问题,这实际上使除法和ArrayList 更糟。我很高兴不断更新它,因此请随时澄清和阐述更多信息。
Numeron

6
应当指出的是,CompressedOops是默认的,现在在所有近期的JDK(7,8和几年的6更新),所以64位不会让一个差异ArrayListLinkedList大小,除非您已明确关闭压缩为哎呀一些原因。
BeeOnRope

1
@jontejj的默认容量增加为50%,因此,当您在ArrayList未指定初始容量的情况下填充时,其使用的内存仍然比少得多LinkedList
霍尔格

243

ArrayList 是你想要的。 LinkedList几乎总是(性能)错误。

为什么LinkedList烂:

  • 它使用许多小型内存对象,因此会影响整个过程的性能。
  • 许多小对象不利于缓存局部性。
  • 任何索引操作都需要遍历,即具有O(n)性能。这在源代码中并不明显,导致算法O(n)的速度比ArrayList
  • 获得良好的性能是棘手的。
  • 即使big-O性能与 ArrayList,反正它也可能会明显变慢。
  • LinkedList在源代码中看到它很令人讨厌,因为这可能是错误的选择。

236
抱歉。使你失望。LinkedList不烂。在某些情况下,LinkedList是要使用的正确类。我同意,在很多情况下它都不比arraylist好,但是它们确实存在。教育做傻事的人!
大卫·特纳

40
很抱歉看到您为此大选。实际上,几乎没有理由使用Java的LinkedList。除了性能不佳外,它还比其他具体的List类使用更多的内存(每个节点都有两个额外的指针,每个节点都是一个单独的包装对象,并带有额外的开销字节)。
凯文·布洛克

42
这是这里最有用的答案之一。令人遗憾的是,许多程序员无法理解(a)抽象数据类型和具体实现之间的区别,以及(b)在确定性能时常量因子和内存开销在现实中的重要性。
Porculus

50
-1:这是一个比较眨眼的视图。是的,的确,ArrayList是一个非常通用的工具。但是,它有其局限性。在某些情况下,这会给您带来麻烦,并且您将不得不使用LinkedList。当然,它是一种非常专业的解决方案,并且,作为任何专业工具,在大多数情况下,它的功能都优于通用的工具。但这并不意味着它会“吸”或类似的东西,您只需要知道何时使用它即可。
马尔科姆

27
@DavidTurner:它们确实存在,但是我想汤姆的意思是,如果您不得不问,您可能想要ArrayList。
user541686

139

作为从事大型SOA Web服务的运营性能工程研究大约十年的人,我希望LinkedList的行为优于ArrayList。尽管LinkedList的稳态吞吐量较差,因此可能导致购买更多硬件-ArrayList的行为在压力下可能导致群集中的应用程序几乎同步地扩展其阵列,并且对于较大的阵列大小,可能导致缺乏响应能力在应用程序中,并且在压力下中断,这是灾难性的行为。

同样,您可以从默认吞吐量的使用期限垃圾收集器中获得更好的应用程序吞吐量,但是一旦获得具有10GB堆的Java应用程序,您就可以在Full GC期间将应用程序锁定25秒,这会导致SOA应用程序超时和失败并在您的SLA频繁发生时将其删除。即使CMS收集器占用更多资源且未达到相同的原始吞吐量,它也是一个更好的选择,因为它具有更可预测的延迟和更小的延迟。

如果性能仅是吞吐量,而您可以忽略延迟,则ArrayList仅是性能的更好选择。根据我的工作经验,我不能忽略最坏情况下的延迟。


8
另一个解决方案不是通过使用ArrayList的suresureCapacity()方法以编程方式管理列表的大小吗?我的问题是,为什么当最好将它们存储在缓存或数据库机制中时,它们会存储在许多脆弱的数据结构中?前几天我接受了一次采访,他们发誓要对ArrayList的弊端大打折扣,但是我来到这里后发现,复杂性分析在各个方面都更好!讨论的重点,思想。谢谢!
ingyhere,2012年

22
一旦获得具有10GB堆的Java应用程序,您就可以在完整GC期间将应用程序锁定25秒,这会导致超时实际上,使用LinkedList 会使您在完整GC期间杀死垃圾收集器,它必须迭代过大的LinkedList并导致缓存未命中每个节点。
bestsss 2014年

5
那是一个可怕的解决方案。您基本上依赖于为您进行GC清理,这是非常昂贵的,这时您只需在arraylist上调用sureCapacity()即可...
Philip Devine

5
@Andreas:A LinkedList 总是分配的内存是普通引用数组的ArrayList五倍,因此即使不回收内存,暂时需要2.5倍的内存仍然消耗少得多的内存。由于大型数组分配绕过了Eden空间,因此它们不会对GC行为产生任何影响,除非确实没有足够的内存,在这种情况下,LinkedList爆炸的时间要早​​得多……
Holger

5
@Andreas另一个问题是如何分配内存。LinkedList只需要一小块可用内存即可分配给下一个元素。ArrayList将需要一个大而连续的自由空间来分配调整大小的数组。如果堆碎片化,则GC可能最终会重新排序整个堆,以释放适当的单个内存块。
Piotr Kusmierczyk '16

128
Algorithm           ArrayList   LinkedList
seek front            O(1)         O(1)
seek back             O(1)         O(1)
seek to index         O(1)         O(N)
insert at front       O(N)         O(1)
insert at back        O(1)         O(1)
insert after an item  O(N)         O(1)

算法:大哦符号

ArrayLists对于一次写入多次读取或追加程序很有用,但不利于从前端或中间进行添加/删除。


42
如果不考虑常量因素,就不能直接比较big-O值。对于小列表(大多数列表很小),ArrayList的O(N)比LinkedList的O(1)快。
Porculus

4
我不在乎小型列表的性能,我的计算机也不在乎,除非以某种方式将其循环使用。
Maarten Bodewes 2011年

45
LinkedList不能真正插入中O(1)。它必须遍历列表的一半才能找到插入点。
Thomas Ahle

8
LinkedList:插入中间O(1)-错误!我发现,即使插入LinkedList大小的1/10位置也比将元素插入ArrayList的1/10位置要慢。更糟糕的是:收集结束。插入ArrayList的最后一个位置(不是最后一个)比LinkedList的最后一个位置(不是最后一个)更快
kachanov 2012年

14
@kachanov插入的LinkedList O(1) ,如果你有一个迭代器插入位置,即ListIterator.add是所谓O(1)LinkedList
已退出-Anony-Mousse

107

是的,我知道,这是一个古老的问题,但是我将投入两分钱:

从性能角度来看,LinkedList 几乎总是错误的选择。有一些非常特定的算法需要使用LinkedList,但是这种算法非常非常少见,该算法通常具体取决于LinkedList在列表中导航后相对快速地在列表中间插入和删除元素的能力。与ListIterator。

在一种常见的使用情况下,LinkedList的性能优于ArrayList:队列的性能。但是,如果您的目标是性能,那么您还应该考虑使用ArrayBlockingQueue(如果可以提前确定队列大小的上限,并且可以负担所有内存的分配),而不要使用LinkedList,或者使用此CircularArrayList实现。(是的,它是从2001年开始的,因此您需要对其进行泛化,但是我得到的性能比与最近的JVM中本文中引用的性能比相当)


39
在Java 6中,您可以使用ArrayDequedocs.oracle.com/javase/6/docs/api/java/util/ArrayDeque.html
Thomas Ahle

1
ArrayDequeLinkedList除非所有操作都在同一末端要慢。用作堆栈是可以的,但是并不能很好地排队。
Jeremy List

2
不正确-至少对于Oracle在jdk1.7.0_60和以下测试中的实现而言。我创建了一个测试,其中我循环了1000万次,而Deque有1000万个随机整数。在循环内部,我从中轮询一个元素并提供一个常量元素。在我的计算机上,LinkedList比ArrayDeque慢10倍以上,并且使用的内存更少。原因是,与ArrayList不同,ArrayDeque保留指向数组头部的指针,这样,在移除头部时不必移动所有元素。
Henno Vermeulen 2015年

6
ArrayDeque可能比Stack用作堆栈时要快,并且比LinkedList用作队列时要快。
akhil_mittal 2015年

3
请注意,akhil_mittal的注释来自ArrayDeque文档。
斯图尔特(Stuart Marks)

65

这是一个效率问题。LinkedList用于添加和删除元素的速度很快,但是访问特定元素的速度很慢。ArrayList用于访问特定元素的速度很快,但添加到任一端的速度可能很慢,尤其是在中间删除时速度很慢。

Array vs ArrayList vs LinkedList vs Vector以及Linked List的深度更深 。


54

正确或不正确:请在本地执行测试,然后自己决定!

中的“编辑/删除”速度LinkedList比快ArrayList

ArrayListArray在大容量应用程序中,需要大小加倍的,由,作为后缀,则更糟。

下面是每个操作的单元测试结果,计时以纳秒为单位。


Operation                       ArrayList                      LinkedList  

AddAll   (Insert)               101,16719                      2623,29291 

Add      (Insert-Sequentially)  152,46840                      966,62216

Add      (insert-randomly)      36527                          29193

remove   (Delete)               20,56,9095                     20,45,4904

contains (Search)               186,15,704                     189,64,981

这是代码:

import org.junit.Assert;
import org.junit.Test;

import java.util.*;

public class ArrayListVsLinkedList {
    private static final int MAX = 500000;
    String[] strings = maxArray();

    ////////////// ADD ALL ////////////////////////////////////////
    @Test
    public void arrayListAddAll() {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX);

        watch.start();
        arrayList.addAll(stringList);
        watch.totalTime("Array List addAll() = ");//101,16719 Nanoseconds
    }

    @Test
    public void linkedListAddAll() throws Exception {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);

        watch.start();
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(stringList);
        watch.totalTime("Linked List addAll() = ");  //2623,29291 Nanoseconds
    }

    //Note: ArrayList is 26 time faster here than LinkedList for addAll()

    ///////////////// INSERT /////////////////////////////////////////////
    @Test
    public void arrayListAdd() {
        Watch watch = new Watch();
        List<String> arrayList = new ArrayList<String>(MAX);

        watch.start();
        for (String string : strings)
            arrayList.add(string);
        watch.totalTime("Array List add() = ");//152,46840 Nanoseconds
    }

    @Test
    public void linkedListAdd() {
        Watch watch = new Watch();

        List<String> linkedList = new LinkedList<String>();
        watch.start();
        for (String string : strings)
            linkedList.add(string);
        watch.totalTime("Linked List add() = ");  //966,62216 Nanoseconds
    }

    //Note: ArrayList is 9 times faster than LinkedList for add sequentially

    /////////////////// INSERT IN BETWEEN ///////////////////////////////////////

    @Test
    public void arrayListInsertOne() {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX + MAX / 10);
        arrayList.addAll(stringList);

        String insertString0 = getString(true, MAX / 2 + 10);
        String insertString1 = getString(true, MAX / 2 + 20);
        String insertString2 = getString(true, MAX / 2 + 30);
        String insertString3 = getString(true, MAX / 2 + 40);

        watch.start();

        arrayList.add(insertString0);
        arrayList.add(insertString1);
        arrayList.add(insertString2);
        arrayList.add(insertString3);

        watch.totalTime("Array List add() = ");//36527
    }

    @Test
    public void linkedListInsertOne() {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(stringList);

        String insertString0 = getString(true, MAX / 2 + 10);
        String insertString1 = getString(true, MAX / 2 + 20);
        String insertString2 = getString(true, MAX / 2 + 30);
        String insertString3 = getString(true, MAX / 2 + 40);

        watch.start();

        linkedList.add(insertString0);
        linkedList.add(insertString1);
        linkedList.add(insertString2);
        linkedList.add(insertString3);

        watch.totalTime("Linked List add = ");//29193
    }


    //Note: LinkedList is 3000 nanosecond faster than ArrayList for insert randomly.

    ////////////////// DELETE //////////////////////////////////////////////////////
    @Test
    public void arrayListRemove() throws Exception {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX);

        arrayList.addAll(stringList);
        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        arrayList.remove(searchString0);
        arrayList.remove(searchString1);
        watch.totalTime("Array List remove() = ");//20,56,9095 Nanoseconds
    }

    @Test
    public void linkedListRemove() throws Exception {
        Watch watch = new Watch();
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(Arrays.asList(strings));

        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        linkedList.remove(searchString0);
        linkedList.remove(searchString1);
        watch.totalTime("Linked List remove = ");//20,45,4904 Nanoseconds
    }

    //Note: LinkedList is 10 millisecond faster than ArrayList while removing item.

    ///////////////////// SEARCH ///////////////////////////////////////////
    @Test
    public void arrayListSearch() throws Exception {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX);

        arrayList.addAll(stringList);
        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        arrayList.contains(searchString0);
        arrayList.contains(searchString1);
        watch.totalTime("Array List addAll() time = ");//186,15,704
    }

    @Test
    public void linkedListSearch() throws Exception {
        Watch watch = new Watch();
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(Arrays.asList(strings));

        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        linkedList.contains(searchString0);
        linkedList.contains(searchString1);
        watch.totalTime("Linked List addAll() time = ");//189,64,981
    }

    //Note: Linked List is 500 Milliseconds faster than ArrayList

    class Watch {
        private long startTime;
        private long endTime;

        public void start() {
            startTime = System.nanoTime();
        }

        private void stop() {
            endTime = System.nanoTime();
        }

        public void totalTime(String s) {
            stop();
            System.out.println(s + (endTime - startTime));
        }
    }


    private String[] maxArray() {
        String[] strings = new String[MAX];
        Boolean result = Boolean.TRUE;
        for (int i = 0; i < MAX; i++) {
            strings[i] = getString(result, i);
            result = !result;
        }
        return strings;
    }

    private String getString(Boolean result, int i) {
        return String.valueOf(result) + i + String.valueOf(!result);
    }
}

1
准确地说,ArrayList不必加倍。请先检查来源。
Danubian Sailor

应该注意的是,您的示例存在缺陷...您正在从以下字符串中删除:18 + [2,12]个字节(“ true0false”,“ true500000false”),平均25个字节,这是元素的大小在中间。众所周知,随着元素字节大小的增加,链表的性能会更好,随着列表大小的增加,连续的数组(列表)会做得更好。最重要的是,您正在对字符串执行.equals()-这不是一个便宜的操作。如果您改用整数,我认为会有所不同。
Centril

2
“…… 在大批量应用中更糟 ”:这是一个误解。LinkedList具有更多的内存开销,因为每个元素都有一个具有五个字段的节点对象。在许多系统上,这会产生20字节的开销。每个元素的平均内存开销为ArrayList一个半字,即6个字节,最坏情况下为8个字节。
Lii 2015年

1
我在这里做了一个更好的基准测试,结果如下:arraylist的附加性能对于您来说是人为降低的,因为addAll给的存储数组的初始大小完全是精确的,因此第一次插入总是触发一个arraycopy。而且,这包括预热周期,以允许在收集数据之前进行JIT编译。
BobMcGee

4
@BillK自Java 8起,您可以使用removeIf(element -> condition)适合的位置ArrayList,与通过迭代器循环和删除相比,适合使用的地方要快得多,因为不需要为每个元素移动整个余数。它的性能好坏LinkedList取决于特定情况,就像LinkedList理论上的O(1)一样,但是仅删除单个节点就需要几次内存访问,这很容易超过ArrayList删除大量元素时所需的数量。 。
Holger

50

ArrayList本质上是一个数组。LinkedList被实现为双链表。

get很清楚。O(1)为ArrayList,因为ArrayList允许使用索引进行随机访问。的O(n)LinkedList,因为它需要首先找到索引。注意:add和的版本不同remove

LinkedList添加和删​​除速度更快,但获取速度较慢。简而言之,在以下情况下LinkedList应首选:

  1. 没有大量的元素随机访问
  2. 有大量的添加/删除操作

=== ArrayList ===

  • 加(E e)
    • 在ArrayList的末尾添加
    • 需要调整内存大小的成本。
    • O(n)最差,O(1)摊销
  • add(int index,E元素)
    • 添加到特定的索引位置
    • 需要转移并可能调整内存大小
    • 上)
  • remove(int index)
    • 删除指定的元素
    • 需要转移并可能调整内存大小
    • 上)
  • remove(对象o)
    • 从此列表中删除第一次出现的指定元素
    • 需要首先搜索元素,然后转移和可能的内存调整大小成本
    • 上)

=== LinkedList ===

  • 加(E e)

    • 添加到列表的末尾
    • O(1)
  • add(int index,E元素)

    • 插入指定位置
    • 需要先找到位置
    • 上)
  • 去掉()
    • 删除列表的第一个元素
    • O(1)
  • remove(int index)
    • 删除具有指定索引的元素
    • 首先需要找到元素
    • 上)
  • remove(对象o)
    • 删除指定元素的第一次出现
    • 首先需要找到元素
    • 上)

这是来自programcreek.com的图(add并且remove是第一种类型,即在列表的末尾添加一个元素,然后在列表中的指定位置删除该元素。):

在此处输入图片说明


3
“ LinkedList比添加/删除快”。错误的,请检查上面stackoverflow.com/a/7507740/638670
Nerrve


34

ArrayList可以随机访问,而LinkedList扩展和删除元素确实很便宜。在大多数情况下,ArrayList都可以。

除非您创建了大列表并解决了瓶颈问题,否则您将永远不必担心差异。


15
LinkedList添加元素并不便宜。将一百万个元素添加到ArrayList几乎总是比将它们添加到LinkedList更快。实际代码中的大多数列表甚至都不到一百万个元素。
Porculus

10
无论何时,您都知道将项目添加到LinkedList的成本。您通常不需要的ArrayList。将单个项目添加到包含一百万个项目的ArrayList中可能会花费很长时间-这是一个O(n)操作,加上两倍的存储空间,除非您预先分配了空间。将项目添加到LinkedList是O(1)。我的最后声明是正确的。
达斯汀

4
不管是100万还是10亿,将单个项添加到ArrayList都是O(1)。将项目添加到LinkedList也是O(1)。“添加”表示添加到结尾。
kachanov

您与我对实现的理解必须有所不同。以我的经验,复制10亿个元素数组比复制100万个元素数组要花费更长的时间。
达斯汀

6
@kachanov,您必须误解Dustin。除非声明了一个十亿个项目的数组,否则最终将需要调整数组的大小,在这种情况下,您将需要将所有元素复制到一个新的更大的数组中,因此有时您会得到O(N),但是对于链表,您将始终得到O(1)
Stan R.

28

由于采用现代计算机体系结构,TL; DRArrayList在几乎所有可能的用例中都将显着提高效率-因此LinkedList应避免使用,除非某些非常独特和极端的情况。


理论上,LinkedList的O(1)为 add(E element)

同样,在列表中间添加元素应该非常有效。

实际情况非常不同,因为LinkedList是一种缓存敌对数据结构。从性能POV来看-在极少数情况下,LinkedList比对Cache友好的性能可能更好 ArrayList

这是基准测试在随机位置插入元素的结果。如您所见-数组列表效率更高,尽管从理论上讲,列表中间的每个插入都需要“移动” 数组的后n个元素(值越低越好):

在此处输入图片说明

在下一代硬件(更大,更高效的缓存)上工作-结果更加确定:

在此处输入图片说明

LinkedList需要更多的时间来完成相同的工作。 代码

这有两个主要原因:

  1. 主要是 -的节点LinkedList随机分散在整个内存中。RAM(“随机访问内存”)并不是真正随机的,需要获取内存块以进行缓存。此操作需要时间,并且在频繁发生此类提取时-需要一直替换高速缓存中的内存页面->高速缓存未命中->高速缓存效率不高。 ArrayList元素存储在连续的内存中-这正是现代CPU架构正在优化的内容。

  2. LinkedList要求保留后退/前进指针的辅助服务器,这意味着与相比,每个存储值的内存消耗是3倍ArrayList

DynamicIntArray btw是一个自定义ArrayList实现,包含Int(原始类型)而不是Objects-因此,所有数据实际上都是相邻存储的-因此效率更高。

需要记住的一个关键因素是,获取存储块的成本比访问单个存储单元的成本更为重要。这就是为什么读取器1MB顺序内存比从不同内存块中读取此数据量快达x400倍的原因:

Latency Comparison Numbers (~2012)
----------------------------------
L1 cache reference                           0.5 ns
Branch mispredict                            5   ns
L2 cache reference                           7   ns                      14x L1 cache
Mutex lock/unlock                           25   ns
Main memory reference                      100   ns                      20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy             3,000   ns        3 us
Send 1K bytes over 1 Gbps network       10,000   ns       10 us
Read 4K randomly from SSD*             150,000   ns      150 us          ~1GB/sec SSD
Read 1 MB sequentially from memory     250,000   ns      250 us
Round trip within same datacenter      500,000   ns      500 us
Read 1 MB sequentially from SSD*     1,000,000   ns    1,000 us    1 ms  ~1GB/sec SSD, 4X memory
Disk seek                           10,000,000   ns   10,000 us   10 ms  20x datacenter roundtrip
Read 1 MB sequentially from disk    20,000,000   ns   20,000 us   20 ms  80x memory, 20X SSD
Send packet CA->Netherlands->CA    150,000,000   ns  150,000 us  150 ms

资料来源:每个程序员都应该知道的延迟数

为了使观点更清楚,请检查将元素添加到列表开头的基准。从理论上讲,这是一个用例,应在理论上LinkedList真正发光,并ArrayList应提供较差甚至更差的结果:

在此处输入图片说明

注意:这是C ++ Std库的基准,但是我以前的经验表明C ++和Java的结果非常相似。源代码

复制连续的内存是现代CPU所优化的一项操作-改变了理论,实际上又使ArrayList/ Vector提高了效率


鸣谢:此处发布的所有基准都是由KjellHedström创建的。可以在他的博客上找到更多数据


我不会称队列为唯一或极端!在LinkedList而不是ArrayList上实现fifo队列要容易得多。实际上,这是ArrayList的噩梦,因为您必须跟踪自己的开始,停止并自行进行重新分配,因此不妨使用数组,但“链表”确实很糟糕。我不确定Java的实现,但是LinkedList可以对队列和出队操作都执行O(1)(需要一个特殊的指向tail元素的指针进行删除,我认为这是Java的,但我没有仔细检查。)
Bill K

24

如果您的代码包含add(0)remove(0),请使用LinkedList和,这是更漂亮的addFirst()removeFirst()方法。否则,使用ArrayList

当然,GuavaImmutableList是您最好的朋友。


3
对于小型列表,ArrayList.add(0)始终总是比LinkedList.addFirst()更快。
Porculus

1
@Porculus我一直在听到这样的论点,即对于较小的列表ArrayList.add(0)会更快,这个很小多少?10个元素,一千万,
garg10may

1
@ garg10may小小于10
杰西威尔逊

@Porculus small表示小于ArrayList下的内部数组的最大容量。
Janac Meena

21

我知道这是一篇过时的文章,但是老实说,我不敢相信没有人提及该LinkedList工具Deque。只需看看Deque(和Queue)中的方法即可;如果你想要一个公平的比较,尝试运行LinkedList反对ArrayDeque,做一个功能的功能比较。


18

这是ArrayListLinkedList和中的Big-O表示法CopyOnWrite-ArrayList

数组列表

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-ArrayList

get                 O(1)
add                 O(n)
contains            O(n)
next                O(1)
remove              O(n)
iterator.remove     O(n)

基于这些,您必须决定选择什么。:)


9
>>>> ArrayList添加-> O(1)<-不是tru。在某些情况下,ArrayList必须增加才能添加一个元素
kachanov

1
LinkedList remove不是O(1),它需要搜索要删除的元素,因此最坏的情况是O(n)和平均值O(n / 2)
garg10may

两者都不是LinkedList.add(),尽管这里的大多数答案都是这样。
user207421 '19

18

让我们比较以下参数的LinkedList和ArrayList:

1.实施

ArrayList是list接口的可调整大小的数组实现,而

LinkedList是列表接口的双链列表实现。


2.表现

  • get(int索引)或搜索操作

    ArrayList get(int index)操作以恒定时间运行,即O(1)而

    LinkedList get(int index)操作的运行时间为O(n)。

    背后的原因的ArrayList比链表更快是ArrayList的使用索引为基础的系统为它的部分,因为它在内部使用一个阵列,在另一方面,

    LinkedList不对其元素提供基于索引的访问,因为它从开始或结束(以较近者为准)进行迭代以检索指定元素索引处的节点。

  • insert()或add(Object)操作

    与ArrayList相比,LinkedList中的插入通常较快。在LinkedList中,添加或插入是O(1)操作。

    ArrayList中,如果数组是完整的(即最坏的情况),则需要调整数组大小并将元素复制到新数组中,这会产生额外的开销,这会使ArrayList中的添加操作运行时间为O(n),否则为O(1) 。

  • remove(int)操作

    LinkedList中的删除操作通常与ArrayList相同,即O(n)。

    LinkedList中,有两个重载的remove方法。一个是remove(),不带任何参数,该参数删除列表的开头并以恒定时间O(1)运行。LinkedList中另一个重载的remove方法是remove(int)或remove(Object),它们删除作为参数传递的Object或int。此方法遍历LinkedList,直到找到对象并将其与原始列表取消链接。因此,此方法的运行时间为O(n)。

    ArrayList中, remove(int)方法涉及将元素从旧数组复制到新的更新数组,因此其运行时间为O(n)。


3.反向迭代器

LinkedList可以使用下降的Iterator()反向反向迭代,而

ArrayList中没有下降的Iterator(),因此我们需要编写自己的代码以反向遍历ArrayList。


4.初始容量

如果构造函数未过载,则ArrayList将创建一个初始容量为10的空列表,而

LinkedList 仅构造没有任何初始容量的空列表。


5.内存开销

LinkedList中的内存开销与ArrayList相比更多,因为中的一个节点需要维护下一个和上一个节点的地址。而

ArrayList中, 每个索引仅保存实际的对象(数据)。


资源


18

我通常会根据要在该特定List上执行的操作的时间复杂性来使用另一个。

|---------------------|---------------------|--------------------|------------|
|      Operation      |     ArrayList       |     LinkedList     |   Winner   |
|---------------------|---------------------|--------------------|------------|
|     get(index)      |       O(1)          |         O(n)       | ArrayList  |
|                     |                     |  n/4 steps in avg  |            |
|---------------------|---------------------|--------------------|------------|
|      add(E)         |       O(1)          |         O(1)       | LinkedList |
|                     |---------------------|--------------------|            |
|                     | O(n) in worst case  |                    |            |
|---------------------|---------------------|--------------------|------------|
|    add(index, E)    |       O(n)          |         O(n)       | LinkedList |
|                     |     n/2 steps       |      n/4 steps     |            |
|                     |---------------------|--------------------|            |
|                     |                     |  O(1) if index = 0 |            |
|---------------------|---------------------|--------------------|------------|
|  remove(index, E)   |       O(n)          |         O(n)       | LinkedList |
|                     |---------------------|--------------------|            |
|                     |     n/2 steps       |      n/4 steps     |            |
|---------------------|---------------------|--------------------|------------|
|  Iterator.remove()  |       O(n)          |         O(1)       | LinkedList |
|  ListIterator.add() |                     |                    |            |
|---------------------|---------------------|--------------------|------------|


|--------------------------------------|-----------------------------------|
|              ArrayList               |            LinkedList             |
|--------------------------------------|-----------------------------------|
|     Allows fast read access          |   Retrieving element takes O(n)   |
|--------------------------------------|-----------------------------------|
|   Adding an element require shifting | o(1) [but traversing takes time]  |
|       all the later elements         |                                   |
|--------------------------------------|-----------------------------------|
|   To add more elements than capacity |
|    new array need to be allocated    |
|--------------------------------------|

ArrayDeque使数组的内容更加平衡,因为插入/删除前/后都是O(1),链表仍然唯一能胜任的是遍历时添加/删除(迭代器操作)。
比尔K,

14

除了上述其他良好的论点外,您还应该注意ArrayListImplements RandomAccess接口,而LinkedListImplements Queue

因此,他们以某种方式解决效率和行为不同的问题(请参见方法列表)。


10

这取决于您将在列表上执行哪些操作。

ArrayList更快地访问索引值。当插入或删除对象时,情况更糟。

要了解更多信息,请阅读任何有关数组和链表之间差异的文章。


2
要了解更多内容,请直接编写代码。并且您会发现ArrayList的实现在插入和删除方面比LinkedList更快。
kachanov

8

数组列表本质上是一个带有添加项目等方法的数组(您应该改用通用列表)。它是可通过索引器访问的项目的集合(例如[0])。它意味着从一项到另一项的进展。

链接列表指定从一项到下一项的进度(项目a->项目b)。您可以使用数组列表获得相同的效果,但是链接列表绝对说明了应该遵循上一个项目的项目。



7

链接列表的一个重要功能(我没有在其他答案中读过)是两个列表的串联。对于数组,这是O(n)(加上某些重新分配的开销),并且只有链表,这仅是O(1)或O(2);-)

重要提示:对于Java,LinkedList这不是事实!请参阅Java中是否有用于链接列表的快速concat方法?


2
那个怎么样?对于链接列表数据结构可能是正确的,但对于Java LinkList对象则不是。您不能仅将a next从一个列表指向第二个列表中的第一个节点。唯一的方法是使用addAll()顺序添加元素,尽管比遍历并调用add()每个元素更好。要在O(1)中快速完成此操作,您将需要一个合成类(例如org.apache.commons.collections.collection.CompositeCollection),但是它适用于任何类型的List / Collection。
凯文·布洛克

是的,没错。我相应地编辑了答案。但看到这个答案“如何”与LinkedList的做到这一点:stackoverflow.com/questions/2494031/...
Karussell

7

ArrayList和LinkedList具有各自的优缺点。

与使用指向下一个节点的指针的LinkedList相比,ArrayList使用连续的内存地址。因此,当您要查找ArrayList中的元素时,比使用LinkedList执行n次迭代要快。

另一方面,在LinkedList中插入和删除要容易得多,因为您只需要更改指针,而ArrayList意味着对任何插入或删除都使用了shift操作。

如果您的应用程序中经常进行检索操作,请使用ArrayList。如果您经常插入和删除,请使用LinkedList。


6

我已经阅读了响应,但是在一种情况下,我总是在想共享的ArrayList上使用LinkedList来听取意见:

每次我有一个方法返回从DB获得的数据列表时,我总是使用LinkedList。

我的理由是,因为无法确切知道我得到了多少结果,所以不会浪费内存(如ArrayList中元素的容量和实际数量之间的差异),并且不会浪费任何时间来尝试复制容量。

就ArrayList而言,我同意至少应始终使用具有初始容量的构造函数,以尽可能减少数组的重复。


5

ArrayListLinkedList这两个工具List interface 和他们的方法和结果几乎是相同的。但是,根据需要,它们之间几乎没有什么区别,一个可以使另一个更好。

ArrayList与LinkedList

1)与Search: ArrayList搜索操作相比,LinkedList搜索操作相当快。get(int index)ArrayList给出性能的O(1)同时,给出的LinkedList性能是O(n)

Reason: ArrayList由于隐式使用数组数据结构,因此可以为其元素维护基于索引的系统,这使得在列表中搜索元素的速度更快。另一方面,LinkedList实现双向链表,该链表需要遍历所有元素以搜索元素。

2)Deletion: LinkedList删除操作可提供O(1)性能,而ArrayList可变性能:O(n)在最坏的情况下(删除第一个元素时)和O(1)在最佳情况下(删除最后一个元素时)。

结论:与ArrayList相比,LinkedList元素的删除速度更快。

原因:LinkedList的每个元素维护两个指针(地址),这些指针指向列表中的两个相邻元素。因此,删除仅需要更改将要删除的节点的两个相邻节点(元素)中的指针位置。而在ArrayList中,所有元素都需要移动以填充由删除的元素创建的空间。

3)Inserts Performance: LinkedListadd方法给出O(1)性能,而在最坏情况下ArrayList给出O(n)。原因与删除原因相同。

4)Memory Overhead: ArrayList维护索引和元素数据,同时LinkedList维护元素数据和两个邻居节点指针

因此,LinkedList中的内存消耗相对较高。

这些类之间的相似之处很少,如下所示:

  • ArrayList和LinkedList都是List接口的实现。
  • 它们都保持元素的插入顺序,这意味着在显示ArrayList和LinkedList元素时,结果集将具有与将元素插入List的顺序相同的顺序。
  • 这两个类都是非同步的,可以使用Collections.synchronizedList方法显式同步。
  • 这些类的iteratorlistIterator返回fail-fast(如果list在创建迭代器之后的任何时间进行结构修改,除非通过iterator’s自己的remove或add方法,否则迭代器将为throwa ConcurrentModificationException)。

什么时候使用LinkedList和什么时候使用ArrayList?

  • 如上所述(O(1))LinkedList与相比,插入和删除操作具有良好的性能ArrayList(O(n))

    因此,如果需要在应用程序中频繁添加和删除,则LinkedList是最佳选择。

  • 搜索(get method)操作快Arraylist (O(1))但不快LinkedList (O(n))

    因此,如果添加和删除操作更少而搜索操作需求更多,则ArrayList将是您最好的选择。


5

ArrayList中的get(i)操作比LinkedList快,原因是:
ArrayList: List接口的可调整大小数组实现
LinkedList: List和Deque接口的双链接列表实现

索引到列表中的操作将从开头或结尾遍历列表,以更接近指定索引的位置为准。


5

1)基础数据结构

ArrayList和LinkedList之间的第一个区别在于,ArrayList由Array支持,而LinkedList由LinkedList支持。这将导致性能上的进一步差异。

2)LinkedList实现双端队列

ArrayList和LinkedList之间的另一个区别是,除了List接口之外,LinkedList还实现了Deque接口,该接口为add()和poll()以及其他几个Deque函数提供了先进先出操作。3)在ArrayList中添加元素如果不触发Array的大小调整,则在ArrayList中添加元素是O(1)操作,在这种情况下,它变为O(log(n)),另一方面,在LinkedList是O(1)操作,因为它不需要任何导航。

4)从某个位置移除元素

为了从特定索引中删除元素(例如,通过调用remove(index)),ArrayList执行复制操作,使其接近O(n),而LinkedList需要遍历该点,这也使其变为O(n / 2) ,因为它可以根据接近度从任一方向来回移动。

5)遍历ArrayList或LinkedList

迭代是LinkedList和ArrayList的O(n)操作,其中n是元素的数量。

6)从位置检索元素

get(index)操作在ArrayList中为O(1),而在LinkedList中为O(n / 2),因为它需要遍历直到该条目。但是,在大O表示法中,O(n / 2)只是O(n),因为我们忽略了那里的常数。

7)记忆

LinkedList使用一个包装对象Entry,这是一个静态嵌套类,用于存储数据以及上一个和下两个节点,而ArrayList仅将数据存储在Array中。

因此,对于ArrayList而言,内存需求似乎比LinkedList少,除了Array在将内容从一个Array复制到另一Array时执行调整大小操作的情况。

如果Array足够大,则此时可能会占用大量内存并触发垃圾回收,这会减慢响应时间。

从ArrayList与LinkedList之间的所有上述差异来看,在几乎所有情况下,ArrayList都是比LinkedList更好的选择,除非您经常执行add()操作而不是remove()或get()。

与ArrayList相比,修改链表更容易,尤其是在开始或结束添加或删除元素时,因为链表内部保留了这些位置的引用,并且可以在O(1)时间内访问它们。

换句话说,您无需遍历链表即可到达要添加元素的位置,在这种情况下,加法变为O(n)操作。例如,在链接列表的中间插入或删除元素。

我认为,将ArrayList而不是LinkedList用作Java中的大多数实际用途。


1
我认为这是整个小组中最好的陈述答案。这是准确而翔实的。我建议更改最后一行,最后添加“除了队列”,这是非常重要的结构,对于链接列表根本没有任何意义。
Bill K

3

我在这里看到的一项测试仅执行一次测试。但是我注意到,您需要多次运行这些测试,最终它们的时间会收敛。基本上,JVM需要预热。对于我的特定用例,我需要将项目添加/删除到增加到约500个项目的列表中。在我的测试中LinkedList,速度更快,LinkedList大约达到50,000 NS ArrayList,大约达到90,000 NS ...付出或接受。请参见下面的代码。

public static void main(String[] args) {
    List<Long> times = new ArrayList<>();
    for (int i = 0; i < 100; i++) {
        times.add(doIt());
    }
    System.out.println("avg = " + (times.stream().mapToLong(x -> x).average()));
}

static long doIt() {
    long start = System.nanoTime();
    List<Object> list = new LinkedList<>();
    //uncomment line below to test with ArrayList
    //list = new ArrayList<>();
    for (int i = 0; i < 500; i++) {
        list.add(i);
    }

    Iterator it = list.iterator();
    while (it.hasNext()) {
        it.next();
        it.remove();
    }
    long end = System.nanoTime();
    long diff = end - start;
    //uncomment to see the JVM warmup and get faster for the first few iterations
    //System.out.println(diff)
    return diff;
}

2

对于ArrayLists和LinkedLists,remove()和insert()的运行时效率均为O(n)。但是,线性处理时间背后的原因来自两个截然不同的原因:

在ArrayList中,您到达O(1)中的元素,但是实际上删除或插入某些内容会使它成为O(n),因为以下所有元素都需要更改。

在LinkedList中,实际上需要O(n)才能到达所需的元素,因为我们必须从头开始,直到达到所需的索引。实际上,删除或插入是常量,因为我们只需为remove()更改1个引用,为insert()更改2个引用。

两者中哪一个插入和删除速度更快,取决于发生的位置。如果我们更接近开始,LinkedList将会更快,因为我们必须经历相对较少的元素。如果我们接近末尾,则ArrayList会更快,因为我们可以在恒定时间内到达那里,而只需要更改跟随它的几个剩余元素即可。当恰好在中间完成LinkedList时,它会更快,因为通过n个元素比移动n个值要快。

奖励:虽然没有办法使这两个方法成为ArrayList的O(1),但实际上在LinkedLists中有一种方法可以做到这一点。假设我们要遍历整个List移除和插入元素。通常,您将使用LinkedList从每个元素的最开始开始,我们也可以“保存”正在使用Iterator的当前元素。借助Iterator,在LinkedList中工作时,remove()和insert()的效率为O(1)。使它成为唯一的性能优势,我知道LinkedList总是比ArrayList更好。


1

ArrayList扩展AbstractList并实现List接口。ArrayList是动态数组。
可以说,它的创建基本上是为了克服数组的缺点

LinkedList类扩展了AbstractSequentialList并实现了List,Deque和Queue接口。
性能
arraylist.get()为O(1),而linkedlist.get()O(n)
arraylist.add()为O(1)和linkedlist.add()0(1)
arraylist.contains()为O(n)和linkedlist.contains()O(n)
arraylist.next()为O(1)和linkedlist.next()O(1)
arraylist.remove()为O(n)而linkedlist.remove()O(1)
在arraylist中
iterator.remove()是O(n)
而链表中
iterator.remove()是O(1)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.