当前位置:  开发笔记 > 编程语言 > 正文

何时在Java中使用LinkedList而不是ArrayList?

如何解决《何时在Java中使用LinkedList而不是ArrayList?》经验,为你挑选了27个好方法。

我一直只是一个人使用:

List names = new ArrayList<>();

我使用接口作为可移植性的类型名称,因此当我问这些问题时,我可以重新编写代码.

何时应该LinkedList使用,ArrayList反之亦然?



1> Jonathan Tra..:

摘要 ArrayListArrayDeque在最好更多的用例比LinkedList.如果你不确定 - 那就开始吧ArrayList.


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

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

对于 LinkedList

get(int index)O(n)(平均n/4步)

add(E element)O(1)

add(int index, E element)O(n)(平均n/4步),但O(1)index = 0 <---主要的好处LinkedList

remove(int index)O(n)(平均n/4步)

Iterator.remove()O(1).<---主要的好处LinkedList

ListIterator.add(E element)O(1) 这是主要的好处之一LinkedList

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

对于 ArrayList

get(int index)O(1) <---主要的好处ArrayList

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允许使用迭代器进行常量插入或删除,但只允许顺序访问元素.换句话说,您可以向前或向后遍历列表,但在列表中查找位置需要的时间与列表的大小成比例.Javadoc说"索引到列表中的操作将从开头或结尾遍历列表,以较近者为准",因此这些方法平均为O(n)(n/4步),但O(1)index = 0.

ArrayList另一方面,允许快速随机读取访问,因此您可以在恒定时间内获取任何元素.但是,除了末端之外的任何地方添加或删除都需要将所有后面的元素移位,以便打开或填补空白.此外,如果您比下面阵列的容量添加更多的元件,一个新的数组(1.5倍的尺寸)被分配,而旧的阵列被复制到新的一个,因此添加到ArrayListO(n)的在最坏的情况但平均不变.

因此,根据您打算执行的操作,您应该相应地选择实现.迭代任何一种List实际上同样便宜.(迭代a在ArrayList技术上更快,但除非你做的事情对性能非常敏感,否则你不应该担心这个 - 它们都是常量.)

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

使用的另一个好处LinkedList出现当您添加或从列表中的头去掉,因为这些操作是O(1) ,而他们是为O(n)进行ArrayList.请注意,这ArrayDeque可能是LinkedList添加和删​​除头部的一个很好的替代方案,但它不是一个List.

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

a的默认初始容量ArrayList非常小(Java 1.4中的10 - 1.8).但由于底层实现是一个数组,因此如果添加大量元素,则必须调整数组大小.当您知道要添加大量元素时,为了避免调整大小的高成本,请构建ArrayList具有更高初始容量的元素.


忘了提到插入费用.在LinkedList中,一旦你有正确的位置,插入成本为O(1),而在ArrayList中它会上升到O(n) - 必须移动经过插入点的所有元素.
没有"O(n/2)"或"O(n/4)"这样的东西.大O符号告诉你如何操作*用更大的*n*来缩放*.并且需要"n/2"步骤的操作与需要"n"步的操作完全相同,这就是为什么删除常数加数或因子的原因.`O(n/2)`和`O(n/4)`都只是'O(n)`.`LinkedList`和`ArrayList`无论如何都有不同的常数因子,因此它不会将一个`O(n/2)`与另一个的O(n/4)`进行比较,两者都只是表示线性缩放操作.
不,对于LinkedList,即使您知道位置,get仍然是O(n),因为为了到达该位置,底层实现必须遍历链表的"下一个"指针以获得该位置的值.没有随机访问这样的东西.对于位置2,步行指针可能很便宜,但对于位置100万,不是那么便宜.关键是,它与位置成正比,这意味着它是O(n).
@Kevin记忆"紧密相连"可能很重要.硬件会将连续的内存块(动态RAM)缓存到L1或L2缓存中更快的静态RAM中.理论上和大多数时候实际上,内存可以被视为随机访问.但实际上,顺序读取内存的速度会比随机顺序快一些.对于性能关键的循环,这可能很重要.他们称之为"空间位置"或[参考地点](http://en.wikipedia.org/wiki/Locality_of_reference).
关于Vector的使用:确实没有必要回归Vector.执行此操作的方法是使用首选的List实现并调用synchronizedList以为其提供同步包装.请参阅:http://java.sun.com/docs/books/tutorial/collections/implementations/wrapper.html
@Kevin内存是随机访问.但是你怎么知道内存中的哪个位置要读?对于数组/ ArrayList,您可以通过将元素大小乘以数组中的位置来计算内存中的地址,因为它们是连续存储的.但是对于LinkedList,你不能这样做.每个元素都存储下一个元素的地址.
这个答案掩盖了LinkedList的最糟糕的方面 - 比ArrayList明显更多的分配.与ArrayList相比,这可以显着增加常量时间.此外,Vector并不是真正的线程安全 - 由于内部非保护状态(Vector已被弃用的原因之一),它仍必须安全发布.
@Andreas,如果你想显示平均值是N/4,那么使用theta表示法,大O不是为了那个
将常量添加到Big-O/Big-Theta以强调额外操作/解释特定情况是可以接受的.例如,当我们谈论从哈希表中读取的复杂性时,您经常会看到:O(1 +加载因子).是的,1表示没有任何渐近,但强调有一个O(1)查找后跟O(加载因子)跟随任何链碰撞哈希值是有用的.在这里,我们写O(n/4)更多来解释_what_"平均"的情况.渐近地,该因素没有区别,但它对于简要总结"平均"对于操作意味着什么是有用的.
@Ken Brock那是'add(int,E)`vs`add(E)`.
使用ArrayList时,您的内存紧密相连,可以提高性能。
@Geek从位置X移除为O(N),因为在进行实际移除之前必须遍历列表以到达相关位置。Iterator.remove为O(1),因为它使用了恒定时间删除,而不必遍历列表(即删除当前元素)。

2> Numeron..:

到目前为止,没有人似乎已经解决了这些列表中每个列表的内存占用量,除了普遍的共识,即a LinkedList"多得多",ArrayList所以我做了一些数字处理以准确地证明两个列表占用了多少N个空引用.

由于在它们的相对系统上引用是32位或64位(即使为空),我已经包含了4组32位LinkedLists和64位数据ArrayLists.

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

注2 :( 感谢BeeOnRope)由于CompressedOops现在默认从JDK6中间开始,因此64位机器的下面的值基本上与它们的32位机器相匹配,除非您特别关闭它.


LinkedList和ArrayList元素的数量x字节


结果清楚地表明,这LinkedList远远超过了ArrayList,特别是元素数量非常多.如果记忆是一个因素,请避开LinkedLists.

我使用的公式如下,让我知道如果我做错了什么我会解决它.32位或64位系统的'b'为4或8,'n'是元素的数量.注意mods的原因是因为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)


你的数学问题是你的图表极大地夸大了影响.你正在建模对象,每个对象只包含一个```int```,所以4或8字节的数据.在链表中,基本上有4个"单词"的开销.因此,您的图表给出了链接列表使用"五次"存储数组列表的印象.这是错的.每个对象的开销为16或32字节,作为附加调整,而不是缩放因子.
没有ArrayList/LinkedList/Node对象只包含一个int,所以我不知道你在那里说什么.我已经将"对象开销"重写为'对象标题'到clarfy - 无论系统如何,每个对象都有一个8字节的标头,是的,它包含了LinkedList中的所有Node对象,这些都是我能够正确计算的.告诉.顺便说一句,再看一遍,我确实在LinkedList中发现了我的数学问题,这实际上使得它和ArrayList*的分数更差*.我很乐意不断更新它,所以请不要犹豫,澄清并精心制作.
应该注意的是,`CompressedOops`现在在所有最近的JDK中都是默认的(7,8和6的更新几年),所以64位不会对`ArrayList`或`LinkedList`大小产生影响,除非你出于某种原因明确地关闭了压缩的oops.

3> Tom Hawtin -..:

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

为什么LinkedList糟透了:

它使用大量小内存对象,因此会影响整个过程的性能.

很多小对象都不利于缓存局部性.

任何索引操作都需要遍历,即具有O(n)性能.这在源代码中并不明显,导致算法O(n)比ArrayList使用的算法慢.

获得良好的表现是棘手的.

即使大O性能相同ArrayList,但无论如何它可能会明显变慢.

LinkedList在源代码中看到它很不耐烦,因为它可能是错误的选择.


抱歉.标记了你.LinkedList不太糟糕.在某些情况下,LinkedList是要使用的正确类.我同意没有很多情况下它比arraylist更好,但它们确实存在.教育做傻事的人!
-1:这是一个相当模糊的观点.是的,ArrayList确实是一个非常通用的工具.但是,它有其局限性.有些情况会导致您遇到麻烦,您将不得不使用LinkedList.当然,它是一种非常专业的解决方案,并且,作为任何专用工具,在大多数情况下,它的性能优于多功能工具.但这并不意味着它"糟透了"或类似的东西,你只需要知道何时使用它.
这是这里最有用的答案之一.令人遗憾的是,许多程序员无法理解(a)抽象数据类型与具体实现之间的区别,以及(b)在确定性能时常数因素和内存开销的实际重要性.
很抱歉看到你为此获得了很多低票.确实没有理由使用Java的LinkedList.除了糟糕的性能之外,它还使用了比其他具体List类更多的内存(每个节点都有两个额外的指针,每个节点都是一个单独的包装器对象,带有额外的开销字节).
@DavidTurner:他们确实存在,但我认为汤姆的观点是,如果你不得不问,你可能想要ArrayList.
@DavidTurner,确实很糟糕.几乎没有一种情况,ArrayList或ArrayDeque的性能会更差.LinkedList是一个内存占用,Large LinkedList可以杀死GC性能.链接结构适用于并发性和LinkedHashMap或LinkedTree之类的东西,但不适用于列表.Tom,您应该包括垃圾收集器必须遍历列表的事实,该列表比阵列支持的结构昂贵得多.旁注:这应该是最重要的答案.
@TimothyShields可能不会使用`LinkedList`.
@TimothyShields,LinkedHashMap/LinkedTree(除了左/右/父节点之外的节点之间的其他链接)是链接结构,但不是*LinkedList*.问题显然是关于juLinkedList vs juArrayList和LinkedList几乎总是一个错误.你很难尝试找到LinkedList实际获胜的例子.
当我第一次看到这个答案时,我猛地回到我的座位上.然后我记得读了很多你的其他答案和你的博客.我心想:也许这家伙有一点意见.然后我花了一天时间思考它.也许我们会对我们的虚拟机,操作系统和CPU针对阵列访问进行优化的程度(以及相应的链接列表)进行折扣.仅这一点就足以影响我的观点.更深层次的问题:同样适用于非VM语言,la C或C++?对此回复的评论数量绝对告诉我这里有更深层次的问题.也许Big-O与VM lang不同?
@DavidTurner,我甚至想不到LinkedList会更好的单一案例.如果您减少内存占用,缺少缓存局部性和额外的对象创建,它会对GC产生严重影响.GC必须实际迭代该LinkedList.即使你试图实现队列或类似的东西,你最好使用循环列表(ArrayDequeue)而不是LinkedList.香草LinkedList只是一个糟糕的选择.
@ TomHawtin-tackline如果你想摊销O(1)获得并执行操作.关键是它是一种数据结构,与数组列表相比具有优点和缺点.对于绝大多数任务,数组列表更好.但有些情况下,链表绝对是正确的选择.
管理内存世界中的很多人似乎甚至都不了解缓存局部的含义,但这是性能方面的正确答案.就特定算法的实现的可读性而言,对于某些场景,链接列表可能更有意义,但顺序存储器是如此优化 - 从HDD一直到CPU上的L1 Cache - 现代系统不会受益从插入/删除时间足以弥补他们积极使用指针.迭代一个数组比不断解除引用"下一个"指针要快得多.
对于大型列表,由于内存膨胀而避免使用LinkedLists,对于使用ArrayLists的小型列表,因为调整大小并不昂贵
@darpet我不跟随.你不是在建议`LinkedList `你呢?
@DavidSoroko对于`LinkedList` vs`ArrayList`可能是正确的.对于前端具有大量操作的工作负载,可能更喜欢"ArrayDeque".是的,JMH是你的朋友.
尝试编写没有双向链接列表的大容量LRU缓存...祝您好运:)

4> lamont..:

作为一个在大规模SOA Web服务上进行操作性能工程大约十年的人,我更喜欢LinkedList相对于ArrayList的行为.虽然LinkedList的稳态吞吐量更差,因此可能会导致购买更多硬件 - ArrayList在压力下的行为可能导致集群中的应用程序在近乎同步的情况下扩展其阵列,并且对于大型阵列可能导致缺乏响应性在应用程序和停电,而在压力下,这是灾难性的行为.

同样,您可以从默认的吞吐量终身垃圾收集器中获得更好的应用程序吞吐量,但是一旦获得具有10GB堆的Java应用程序,您可以在完整的GC期间锁定应用程序25秒,这会导致SOA应用程序超时和失败如果太频繁发生,你的SLA就会受到影响.即使CMS收集器占用更多资源并且无法实现相同的原始吞吐量,但它是一个更好的选择,因为它具有更可预测和更小的延迟.

如果性能的全部意思是吞吐量而您可以忽略延迟,那么ArrayList只是性能的更好选择.根据我的工作经验,我不能忽视最坏情况的延迟.


*一旦你获得了10GB堆的java应用程序,你可以在完整的GC期间锁定应用程序25秒,这会导致超时*实际上使用LinkedList你在完整的GC期间杀死垃圾收集器,它必须用缓存迭代过大的LinkedList错过每个节点.
另一种解决方案是不是通过使用ArrayList的ensureCapacity()方法以编程方式管理列表的大小?我的问题是,当它们最好存储在缓存或数据库机制中时,为什么有这么多东西被存储在一堆脆弱的数据结构中?前几天我接受了一次采访,他们就ArrayList的邪恶起伏不定,但我来到这里,我发现复杂性分析更全面!很重要的讨论点.谢谢!
@Andreas另一个问题是如何分配内存.`LinkedList`只需要一小段空闲内存即可分配给下一个元素.`ArrayList`需要一个大的_continous_空闲空间块来分配调整大小的数组.如果堆碎片化,那么GC最终可能会重新排序整个堆,以释放合适的单个内存块.
那是......一个可怕的解决方案.当你只需要在arraylist上调用ensureCapacity()时,你基本上依赖于GC清理你,这是非常昂贵的......
@Andreas:`LinkedList`*总是*分配内存的五倍而不是简单的引用数组,因此临时需要2.5次的`ArrayList`仍然会消耗更少的内存,即使内存没有回收.由于大型数组分配绕过了Eden空间,它们对GC行为没有任何影响,除非内存确实不足,在这种情况下,`LinkedList`更早爆炸......
@Holger问题不是总使用量,而是分配时的总和。在“ LinkedList”中添加一项会*每次*分配少量新引用。我可以观察资源分配的趋势,并在需要时计划维护中断。将一个项目添加到ArrayList通常不会分配新的引用,并且*有时会分配数百万*。我无法通过查看仪表板来预测这种行为,也无法计划jvm何时需要新资源。

5> Michael Muns..:
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)

算法:Big-Oh表示法

ArrayLists适用于一次性写入多次读取或appender,但在前面或中间添加/删除时效果不佳.


LinkedList不能真正插入到'O(1)`中间.它必须遍历列表的一半才能找到插入点.
如果不考虑常数因素,就不能直接比较big-O值.对于小列表(并且大多数列表很小),ArrayList的O(N)比LinkedList的O(1)快.
@kachanov如果你有插入位置**的迭代器,那么插入`LinkedList`*是*`O(1)`**,即`ListIterator.add`对于`LinkedList`应该是`O(1)` .
LinkedList:插入中间O(1) - 错误!我发现即使插入LinkedList大小的1/10位置也比将元素插入ArrayList的1/10位置慢.更糟糕的是:收集的结束.插入到ArrayList的最后位置(不是最后一个位置)比在LinkedList的最后位置(不是最后位置)更快
我不关心小列表的性能,也不关心我的计算机*除非*它以某种方式在循环中使用.
很好的答案.虽然是一件小事:对于`ArrayList`,后面插入并不总是O(1).也许大多数时候.但是当它需要重新分配内部阵列时,将发生完整拷贝并且将产生O(n)的成本.
真正.这仍然被认为是摊销的O(1),即http://anh.cs.luc.edu/363/notes/06A_Amortizing.html

6> Daniel Marti..:

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

在性能方面,LinkedList 几乎总是错误的选择.有一些非常具体的算法需要一个LinkedList,但那些非常非常罕见,并且算法通常特别依赖于LinkedList能够相对快速地插入和删除列表中间的元素,一旦你在那里导航使用ListIterator.

有一个常见的用例,其中LinkedList优于ArrayList:队列的那个.但是,如果您的目标是性能,而不是LinkedList,您还应该考虑使用ArrayBlockingQueue(如果您可以提前确定队列大小的上限,并且可以预先分配所有内存),或者此CircularArrayList实现.(是的,它是从2001年开始的,所以你需要对它进行一般化,但我的性能比率与刚刚在最近的JVM中引用的内容相当)


从Java 6开始,您可以使用`ArrayDeque`.http://docs.oracle.com/javase/6/docs/api/java/util/ArrayDeque.html
当用作堆栈时,"ArrayDeque"可能比"Stack"更快,并且当用作队列时比"LinkedList"更快.
注意,akhil_mittal的注释来自ArrayDeque文档。

7> dgtized..:

这是一个效率问题.LinkedList添加和删​​除元素的速度很快,但访问特定元素的速度很慢.ArrayList访问特定元素的速度很快,但添加到任何一端都很慢,特别是在中间删除速度很慢.

Array和ArrayList vs LinkedList vs Vector的内容更加深入, Linked List也是如此.



8> Ash..:

正确或不正确:请在本地执行测试并自行决定!

编辑/删除速度LinkedListArrayList.

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 stringList = Arrays.asList(strings);
        List arrayList = new ArrayList(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 stringList = Arrays.asList(strings);

        watch.start();
        List linkedList = new LinkedList();
        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 arrayList = new ArrayList(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 linkedList = new LinkedList();
        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 stringList = Arrays.asList(strings);
        List arrayList = new ArrayList(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 stringList = Arrays.asList(strings);
        List linkedList = new LinkedList();
        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 stringList = Arrays.asList(strings);
        List arrayList = new ArrayList(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 linkedList = new LinkedList();
        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 stringList = Arrays.asList(strings);
        List arrayList = new ArrayList(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 linkedList = new LinkedList();
        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);
    }
}


@BillK自Java 8起,您可以在适合的地方使用`removeIf(element-> condition)`,与通过迭代器循环和删除相比,对于'ArrayList'而言它可以显着更快,因为不需要移动整个每个元素的其余部分。它的性能好于或劣于LinkedList取决于特定的情况,因为从理论上讲,LinkedList在O(1)上,但是仅删除单个节点就需要几次内存访问,这很容易超出`所需的数量。删除大量元素时使用ArrayList`。
“ ... *在大批量应用中更糟*”:这是一个误解。LinkedList具有更多的内存开销,因为每个元素都有一个具有五个字段的节点对象。在许多系统上,这会产生20字节的开销。ArrayList的每个元素的平均内存开销为一个半字,占6个字节,最坏情况下占8个字节。

9> Ryan..:

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

get是相当清楚的.O(1)for ArrayList,因为ArrayList允许使用索引进行随机访问.O(n)for LinkedList,因为它需要先找到索引.注意:有不同版本的addremove.

LinkedList添加和删​​除速度更快,但获取速度更慢.简而言之,LinkedList如果:

    没有大量的随机访问元素

    有大量的添加/删除操作

=== ArrayList ===

添加(E e)

在ArrayList的末尾添加

需要内存调整成本.

O(n)最差,O(1)摊销

add(int index,E element)

添加到特定的索引位置

需要转移和可能的内存调整成本

上)

remove(int index)

删除指定的元素

需要转移和可能的内存调整成本

上)

删除(对象o)

从此列表中删除指定元素的第一个匹配项

需要首先搜索元素,然后转移和可能的内存调整大小成本

上)

=== LinkedList ===

添加(E e)

添加到列表的末尾

O(1)

add(int index,E element)

插入指定位置

需要先找到位置

上)

去掉()

删除列表的第一个元素

O(1)

remove(int index)

删除具有指定索引的元素

需要先找到元素

上)

删除(对象o)

删除指定元素的第一个匹配项

需要先找到元素

上)

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

在此输入图像描述


"LinkedList比添加/删除更快".错了,请查看上面的答案http://stackoverflow.com/a/7507740/638670

10> Ruslan..:

LinkedList的作者Joshua Bloch:

有没有人真正使用LinkedList?我写了它,我从来没用过它.

链接:https://twitter.com/joshbloch/status/583813919019573248

我很抱歉答案不像其他答案那样提供信息,但我认为这将是最有趣和不言自明的.


我来到这个主题只是为了检查Joshua的推文是否被提及.它是 :)

11> Dustin..:

ArrayList随机访问,而LinkedList扩展和删除元素真的很便宜.对于大多数情况,ArrayList很好.

除非您创建了大型列表并测量了瓶颈,否则您可能永远不必担心差异.


LinkedList对于添加元素并不便宜.向ArrayList添加一百万个元素几乎总是比将它们添加到LinkedList更快.实际代码中的大多数列表甚至都不是一百万个元素.
在任何给定的点,您都知道将项添加到LinkedList的成本.你没有的ArrayList(一般来说).将单个项添加到包含一百万个*的ArrayList*可能需要很长时间 - 除非您预先分配空间,否则它是O(n)操作加上存储的两倍.将项添加到LinkedList是O(1).我最后的发言是.
@kachanov你必须误解达斯汀.除非你已经声明了一个包含10亿个项目的数组,否则你最终需要调整数组的大小,在这种情况下你需要将所有元素复制到一个新的更大的数组中,因此有时候你会得到O(N),但是有了链接列表,你将永远得到O(1)
将单个项添加到ArrayList是O(1),无论它是100万还是10亿.将项添加到LinkedList也是O(1)."添加"意味着添加到最后.

12> Jesse Wilson..:

如果你的代码有add(0)remove(0),使用a LinkedList和它更漂亮addFirst()removeFirst()方法.否则,请使用ArrayList.

当然,番石榴的ImmutableList是你最好的朋友.


对于小列表,ArrayList.add(0)仍然总是比LinkedList.addFirst()更快.

13> Lior Bar-On..:

TL;由于现代计算机体系结构的DR,ArrayList对于几乎任何可能的用例都将显着提高效率 - 因此LinkedList除了一些非常独特和极端的情况之外应该避免.


理论上,LinkedList有一个O(1) add(E element)

在列表中间添加元素应该非常有效.

实践是非常不同的,因为LinkedList是Cache Hostile Data结构.从性能POV - 很少有LinkedList可能比Cache友好的 表现更好的情况ArrayList.

以下是在随机位置插入元素的基准测试结果.正如您所看到的 - 数组列表如果效率更高,尽管理论上列表中间的每个插入都需要"移动" 数组中较晚的n个元素(较低的值更好):

在此输入图像描述

使用更新一代的硬件(更大,更高效的缓存) - 结果更具决定性:

在此输入图像描述

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

这有两个主要原因:

    主要是 - 节点的节点LinkedList随机分散在内存中.RAM("随机存取存储器")实际上并不是随机的,需要将存储器块提取到缓存.此操作需要时间,并且当此类提取频繁发生时 - 缓存中的内存页面需要一直更换 - >缓存未命中 - >缓存效率不高. ArrayList元素存储在连续内存中 - 这正是现代CPU架构所优化的内容.

    LinkedList保持后退/前进指针所需的辅助,这意味着与存储的每个值相比,存储器消耗的3倍ArrayList.

DynamicIntArray,btw,是一个自定义的ArrayList实现,保存Int(基本类型)而不是对象 - 因此所有数据实际上都是相邻存储的 - 因此效率更高.

要记住的关键要素是获取内存块的成本比访问单个内存单元的成本更重要.这就是为什么读取器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 lib的基准测试,但我之前的经验表明C++和Java结果非常相似.源代码

复制顺序大量内存是由现代CPU优化的操作 - 改变理论并实际制作,再次ArrayList/ Vector更高效


致谢:此处发布的所有基准测试均由KjellHedström创建.他的博客上可以找到更多数据



14> Ajax..:

我知道这是一个老帖子,但老实说,我不敢相信没人提到这个LinkedList工具Deque.只需看看Deque(和Queue)中的方法; 如果你想要一个公平的比较,尝试运行LinkedList反对ArrayDeque,做一个功能的功能比较.



15> Rajith Delan..:

这是在两个大O符号ArrayListLinkedListCopyOnWrite-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)

写入时复制-的ArrayList

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

基于这些,你必须决定选择什么.:)


>>>> ArrayList add - > O(1)< - not tru.在某些情况下,ArrayList必须增长以添加一个元素

16> Abhijeet Ash..:

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

1.实施

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

LinkedList是列表界面的双向链表实现.


2.表现

get(int index)或搜索操作

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

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

ArrayList比LinkedList更快的原因是ArrayList使用基于索引的系统作为其元素,因为它在内部使用数组数据结构,另一方面,

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

insert()或add(Object)操作

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

ArrayList中,如果数组是完整的,即最坏的情况,则需要额外调整数组大小和将元素复制到新数组的成本,这使得在ArrayList O(n)中添加运算的运行时,否则它是O(1) .

remove(int)操作

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

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

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


3.反向迭代器

LinkedList可以使用descendingIterator()while在反向迭代

ArrayList中没有descendingIterator(),所以我们需要编写自己的代码来反向迭代ArrayList.


4.初始容量

如果构造函数没有重载,则ArrayList会创建一个初始容量为10的空列表

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


5.内存开销

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

ArrayList中, 每个索引只保存实际对象(数据).


资源



17> Gayan Weerak..:

基于我在特定列表上执行的操作的时间复杂性,我通常使用一个.

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



18> PhiLho..:

除了上面的其他好的参数,你应该注意ArrayListimplements RandomAccess接口,而LinkedList实现Queue.

因此,它们以某种方式解决了稍微不同的问题,效率和行为的差异(参见他们的方法列表).



19> Matthew Schi..:

这取决于你将在List上做更多的操作.

ArrayList访问索引值的速度更快.插入或删除对象时要糟糕得多.

要了解更多信息,请阅读任何讨论数组和链接列表之间差异的文章.


要找到更多不读,只需编写代码.你会发现ArrayList实现比插入和删除中的LinkedList更快.

20> chharvey..:

请参阅Java教程 - 列表实现.


嗨@chharvey,仅链接答案可获得6个支持?请添加一些可以支持该链接的点。如果oracle更改其链接怎么办?

21> kemiller2002..:

数组列表本质上是一个数组,其中包含添加项等的方法(您应该使用通用列表).它是可以通过索引器访问的项目集合(例如[0]).它意味着从一个项目到下一个项目的进展.

链表指定从一个项目到下一个项目的进展(项目a - >项目b).您可以使用数组列表获得相同的效果,但链接列表绝对说明了应该跟随前一个项目的项目.



22> Karussell..:

链表的一个重要特征(我在另一个答案中没有读到)是两个列表的串联.对于数组,这是O(n)(一些重新分配的开销),链接列表只有O(1)或O(2);-)

重要:对于Java来说,LinkedList这不是真的!请参阅Java中的链表是否有快速连接方法?


那个怎么样?对于链表数据结构而言,这可能是真的,但不是Java LinkList对象.您不能只将一个列表中的"next"指向第二个列表中的第一个节点.唯一的方法是使用`addAll()`顺序添加元素,虽然它比循环更好并为每个元素调用`add()`.要在O(1)中快速完成此操作,您需要一个合成类(如org.apache.commons.collections.collection.CompositeCollection),但这适用于任何类型的List/Collection.

23> gaijinco..:

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

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

我的理由是因为不可能确切地知道我得到了多少结果,所以不会浪费内存(如在ArrayList中容量和实际元素数量之间的差异),并且没有浪费时间去尝试复制容量.

至于ArrayList,我同意至少你应该总是使用具有初始容量的构造函数,以尽可能地减少数组的重复.



24> Nesan Mano..:

ArrayList和LinkedList各有利弊.

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

另一方面,LinkedList中的插入和删除更容易,因为您只需更改指针,而ArrayList意味着使用shift操作进行任何插入或删除.

如果您的应用程序中有频繁的检索操作,请使用ArrayList.如果频繁插入和删除,请使用LinkedList.



25> Amitābha..:

ArrayList中的操作get(i)比LinkedList快,因为:
ArrayList: List接口的Resizable-array实现
LinkedList: List和Deque接口的双链表实现

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



26> Real73..:

ArrayListLinkedList这两个工具List interface 和他们的方法和结果几乎是相同的.然而,它们之间几乎没有差异,这取决于要求,使一个优于另一个.

ArrayList与LinkedList

1)与Search: ArrayList搜索操作相比,LinkedList搜索操作非常快.get(int index)in ArrayList赋予性能O(1)同时LinkedList表现O(n).

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

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

结论:与ArrayList相比,LinkedList元素删除更快.

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

3)Inserts Performance: LinkedList添加方法给出O(1)的性能,同时ArrayList使O(n)在最坏的情况.原因与删除说明相同.

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

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

这些类之间几乎没有相似之处,如下所示:

ArrayList和LinkedList都是List接口的实现.

它们都维护元素的插入顺序,这意味着在显示ArrayList和LinkedList元素时,结果集将具有将元素插入List的相同顺序.

这两个类都是非同步的,可以使用Collections.synchronizedList方法显式同步.

iteratorlistIterator返回的这些类fail-fast(如果名单在任何时间从结构上修改创建迭代器之后,以任何方式,除了通过iterator’s自身的remove或add方法,迭代器都将throwConcurrentModificationException).

何时使用LinkedList以及何时使用ArrayList?

由于插入上述解释和删除操作提供良好的性能(O(1))LinkedList相比ArrayList(O(n)).

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

Search(get method)操作很快Arraylist (O(1))但不在LinkedList (O(n))

因此,如果添加和删除操作较少以及搜索操作要求较多,则ArrayList将是您最好的选择.



27> 小智..:

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),因为它需要遍历直到该条目。不过,在Big 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中的大多数实际用途。

推荐阅读
郑谊099_448
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有