何时在Java中使用LaKeDLIST表上的ARARYLIST?

When to use LinkedList over ArrayList in Java?

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

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

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

何时应将LinkedList用于ArrayList之上,反之亦然?


总结:在更多的用例中,使用ArrayDequeArrayList比使用LinkedList更好。如果您不确定,请从ArrayList开始。好的。

LinkedListArrayList是列表接口的两种不同实现。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步,最佳情况下需要恒定的步数(如index=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个步骤,最佳情况下的步骤数不变(列表结尾),最差情况下的步骤数不变(列表开头)。好的。

LinkedList允许使用迭代器进行固定时间的插入或删除,但只允许按顺序访问元素。换句话说,您可以向前或向后浏览列表,但是在列表中查找位置需要与列表大小成比例的时间。javadoc说"索引到列表中的操作将从开始或结束遍历列表,以更接近的为准",因此这些方法平均为O(n)(n/4步),而对于index = 0则为O(1步)。好的。

另一方面,ArrayList允许快速随机读取,因此您可以在恒定时间内获取任何元素。但是,除了结尾之外,任何地方的添加或删除都需要将后面的所有元素转换过来,要么打开一个缺口,要么填补这个缺口。此外,如果添加的元素多于底层数组的容量,则会分配一个新数组(大小的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的每个元素都有更多的开销,因为指向下一个和上一个元素的指针也被存储。ArrayLists没有这个开销。然而,无论元素是否实际添加,ArrayLists占用的内存与为容量分配的内存一样多。好的。

EDCOX1×0的默认初始容量非常小(从Java 1.4到1.8的10)。但是,由于底层实现是一个数组,如果添加了大量元素,则必须调整数组的大小。为了避免在您知道要添加大量元素时调整大小的高成本,请使用更高的初始容量构建ArrayList。好的。好啊。


到目前为止,除了普遍认为LinkedListArrayList要"多得多"之外,似乎没有人解决每个列表的内存占用问题,所以我做了一些数字运算来精确地证明两个列表对n个空引用的占用量。

由于引用在它们的相关系统上是32位或64位(即使为空),所以我为32位和64位LinkedListsArrayLists包含了4组数据。

注:ArrayList行所示的大小是用于修剪列表的-实际上,ArrayList中的支持数组的容量通常大于其当前元素计数。

注2:(感谢BeeonRope)由于compressedoops现在是JDK6和更高版本的默认值,64位机器下面的值基本上与32位机器匹配,除非您特别关闭它。

Graph of LinkedList and ArrayList No. of Elements x Bytes

结果表明,LinkedListArrayList大得多,尤其是元素计数很高。如果记忆是一个因素,那就避开LinkedLists

我使用的公式如下,如果我做错了,请告诉我,我会解决的。"对于32位或64位系统,"b"是4或8,而"n"是元素数。注意MODS的原因是因为Java中的所有对象将占用8字节空间的倍数,而不管它是否被全部使用。

ArrayList:

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:

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)


你想要的是ArrayListLinkedList几乎总是一个(性能)错误。

为什么LinkedList很糟糕:

  • 它使用许多小内存对象,因此会影响整个进程的性能。
  • 许多小对象对缓存位置不好。
  • 任何索引操作都需要遍历,即具有O(N)性能。这在源代码中并不明显,导致算法o(n)比使用ArrayList慢。
  • 要想取得好成绩是很困难的。
  • 即使当big-o的性能与ArrayList相同时,无论如何,它可能会慢得多。
  • 在源代码中看到LinkedList是令人不安的,因为这可能是错误的选择。


作为在非常大规模的SOA Web服务上进行操作性能工程大约十年的人,我更喜欢LinkedList的行为而不是ArrayList。尽管LinkedList的稳定状态吞吐量更差,因此可能导致购买更多的硬件,但在压力下,ArrayList的行为可能导致集群中的应用程序近同步地扩展其阵列,而对于大的阵列大小,则可能导致应用程序缺乏响应能力和停机,而在压力下,这就是CA可口的行为。

类似地,您可以从默认吞吐量持久的垃圾收集器中获得更好的吞吐量,但是一旦您获得10GB堆的Java应用程序,您就可以在一个完整的GCS中锁定应用程序25秒,这会导致SOA应用程序中的超时和故障,并且如果频繁出现,则将SLAS吹走。尽管CMS收集器需要更多的资源,并且不能实现相同的原始吞吐量,但它是一个更好的选择,因为它具有更高的可预测性和更小的延迟。

如果您所说的性能就是吞吐量,并且可以忽略延迟,那么对于性能而言,arraylist只是一个更好的选择。根据我在工作中的经验,我不能忽视最坏情况下的延迟。


1
2
3
4
5
6
7
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)

算法:大哦符号

arraylist适合一次写入、多次读取或追加,但不适合从前面或中间添加/删除。


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

LinkedList几乎总是错误的选择,从性能上来说。有一些非常具体的算法需要一个LinkedList,但是这些算法非常非常罕见,而且算法通常会特别依赖于LinkedList在列表中间插入和删除元素的能力,只要您使用ListIterator导航到那里。

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


这是一个效率问题。LinkedList在添加和删除元素时速度很快,但在访问特定元素时速度很慢。EDCOX1·1的访问对于访问特定的元素来说是快速的,但是可以很慢地添加到两端,尤其是在中间删除的速度较慢。

array vs arraylist vs linkedlist vs vector更深入链表。


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

LinkedList中的编辑/删除比ArrayList更快。

Array支持的ArrayList在大批量应用中更差,需要双倍的大小。

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

1
2
3
4
5
6
7
8
9
10
11
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

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
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);
    }
}


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

get相当清楚。o(1)对于ArrayList,因为ArrayList允许使用索引进行随机访问。o(n)表示LinkedList,因为它需要先找到索引。注:addremove有不同版本。

LinkedList的添加和删除速度更快,但get速度较慢。简言之,在以下情况下,应优先选择LinkedList

  • 元素没有大量的随机访问
  • 有大量的添加/删除操作
  • ===数组列表===

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

    ===链接列表===

    • 添加(e)

      • 添加到列表末尾
      • O(1)
    • 加法(int index,e element)

      • 在指定位置插入
      • 需要先找到位置
      • o(n)
    • 移除()
      • 删除列表的第一个元素
      • O(1)
    • 删除(int index)
      • 删除具有指定索引的元素
      • 需要先找到元素
      • o(n)
    • 删除(对象O)
      • 删除指定元素的第一个匹配项
      • 需要先找到元素
      • o(n)

    下面是programcreek.com的一个图(addremove是第一种类型,即在列表末尾添加一个元素,并在列表中的指定位置删除该元素):

    enter image description here


    1)搜索:与LinkedList搜索操作相比,ArrayList搜索操作速度相当快。ArrayList中的get(int index)给出了o(1)的性能,而LinkedList的性能是o(n)。

    原因:array list为其元素维护了一个基于索引的系统,因为它隐式地使用数组数据结构,这使得它可以更快地搜索列表中的元素。另一方面,LinkedList实现了一个双重链接列表,需要遍历所有元素以搜索元素。

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

    结论:linkedList元素删除速度快于arraylist。

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

    3)插入性能:LinkedList Add方法提供O(1)性能,而ArrayList在最坏情况下提供O(n)。原因与删除的解释相同。

    4)内存开销:ArrayList维护索引和元素数据,而LinkedList维护元素数据和相邻节点的两个指针,因此LinkedList内存消耗相对较高。

    这些类别之间的相似之处如下:

    ArrayList和LinkedList都是列表接口的实现。它们都保持元素插入顺序,这意味着在显示arraylist和linkedlist元素时,结果集的顺序与元素插入列表的顺序相同。这两个类都是非同步的,可以使用collections.synchronizedList方法显式同步。这些类返回的迭代器和ListIterator很快失败(如果在创建迭代器之后的任何时候对列表进行结构修改,则除了通过迭代器自己的移除或添加方法之外,迭代器将以任何方式引发ConcurrentModificationException)。

    何时使用LinkedList,何时使用ArrayList?

    1)如上所述,与arraylist(o(n))相比,insert和remove操作在linkedlist中具有良好的性能(o(1))。因此,如果应用程序中需要频繁添加和删除,那么LinkedList是最佳选择。

    2)搜索(get-method)操作在arraylist(o(1))中速度很快,但在linkedlist(o(n))中速度不快,因此,如果添加和删除操作较少,并且搜索操作要求更多,arraylist将是您的最佳选择。


    Joshua Bloch,LinkedList的作者:

    Does anyone actually use LinkedList? I wrote it, and I never use it.

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

    我很抱歉这个答案没有其他答案那么有信息量,但我认为这将是最有趣和最不言自明的。


    ArrayList是随机访问的,而LinkedList的扩展和删除元素的成本非常低。在大多数情况下,ArrayList是好的。

    除非您已经创建了大量的列表并测量了瓶颈,否则您可能永远不需要担心差异。


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

    当然,瓜娃的不变清单是你最好的朋友。


    我知道这是一个古老的职位,但我真的不敢相信没有人提到LinkedList实现Deque。只需查看DequeQueue中的方法;如果您想要一个公平的比较,尝试运行LinkedListArrayDeque进行比较,并做一个特性进行特性比较。


    以下是ArrayListLinkedList以及CopyOnWrite-ArrayList中的big-o符号:

    数组列表

    1
    2
    3
    4
    5
    6
    get                 O(1)
    add                 O(1)
    contains            O(n)
    next                O(1)
    remove              O(n)
    iterator.remove     O(n)

    链表

    1
    2
    3
    4
    5
    6
    get                 O(n)
    add                 O(1)
    contains            O(n)
    next                O(1)
    remove              O(1)
    iterator.remove     O(1)

    copyonwrite数组列表

    1
    2
    3
    4
    5
    6
    get                 O(1)
    add                 O(n)
    contains            O(n)
    next                O(1)
    remove              O(n)
    iterator.remove     O(n)

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


    让我们比较以下参数LinkedList和ArrayList w.r.t.:

    1。实施

    ArrayList is the resizable array implementation of list interface , while

    LinkedList is the Doubly-linked list implementation of the list interface.

    2。性能

    • 获取(int index)或搜索操作

      ArrayList get(int index) operation runs in constant time i.e O(1) while

      LinkedList get(int index) operation run time is O(n) .

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

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

    • insert()或add(object)操作

      Insertions in LinkedList are generally fast as compare to ArrayList. In LinkedList adding or insertion is O(1) operation .

      While in ArrayList, if the array is the full i.e worst case, there is an extra cost of resizing array and copying elements to the new array, which makes runtime of add operation in ArrayList O(n), otherwise it is O(1).

    • 删除(int)操作

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


      In LinkedList, there are two overloaded remove methods. one is remove() without any parameter which removes the head of the list and runs in constant time O(1). The other overloaded remove method in LinkedList is remove(int) or remove(Object) which removes the Object or int passed as a parameter. This method traverses the LinkedList until it found the Object and unlink it from the original list. Hence this method runtime is O(n).

      While in ArrayList remove(int) method involves copying elements from the old array to new updated array, hence its runtime is O(n).

    三。逆向迭代器

    LinkedList can be iterated in reverse direction using descendingIterator() while

    there is no descendingIterator() in ArrayList , so we need to write our own code to iterate over the ArrayList in reverse direction.

    4。初始容量

    If the constructor is not overloaded, then ArrayList creates an empty list of initial capacity 10, while

    LinkedList only constructs the empty list without any initial capacity.

    5。内存开销

    Memory overhead in LinkedList is more as compared to ArrayList as a node in LinkedList needs to maintain the addresses of the next and previous node. While

    In ArrayList each index only holds the actual object(data).

    来源


    由于现代计算机体系结构的原因,ArrayList对于几乎所有可能的用例都将显著提高效率,因此除了一些非常独特和极端的用例外,应避免LinkedList

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

    另外,在列表中间添加元素应该是非常有效的。

    实践是非常不同的,因为LinkedList是一个缓存敌对的数据结构。从性能POV来看,很少有情况下,LinkedList比缓存友好的ArrayList性能更好。

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

    enter image description here

    在新一代硬件上工作(更大、更高效的缓存)-结果更具决定性:

    enter image description here

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

    这主要有两个原因:

  • 主要是-EDOCX1的节点(0)随机分布在内存中。RAM("随机存取存储器")不是真正随机的,需要将内存块提取到缓存中。此操作需要花费时间,并且当此类提取频繁发生时,需要始终替换缓存中的内存页->缓存未命中->缓存无效。ArrayList元素存储在连续内存中,这正是现代CPU体系结构优化的目标。

  • 二级LinkedList需要保留/转发指针,这意味着存储的每个值的内存消耗是ArrayList的3倍。

  • 动态tarray,btw,是一个自定义的arraylist实现,包含Int而不是对象,因此所有数据都是相邻存储的,因此效率更高。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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应该呈现出糟糕甚至更糟的用例结果:

    enter image description here

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

    复制一个连续的大容量内存是一个由现代CPU变化理论优化的操作,实际上使ArrayListVector更加高效。

    学分:这里发布的所有基准都是由Kjell Hedstr创建的?M.在他的博客上可以找到更多的数据。


    除了上面的其他好论点,您应该注意到ArrayList实现RandomAccess接口,而LinkedList实现Queue接口。

    所以,不知何故,他们解决的问题略有不同,效率和行为也有所不同(见他们的方法列表)。


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


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

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


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

    重要的是:对于Java,它的EDCOX1(0)这是不正确的!在Java中有一个链表的快速CONTAT方法吗?


    这取决于您将在列表中做更多的操作。

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

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


    我已经阅读了回复,但有一种情况是,我总是使用一个LinkedList,而不是一个ArrayList,我想分享它来听取意见:

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

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

    对于数组列表,我同意至少应该始终使用具有初始容量的构造函数,以尽可能减少数组的重复。


    我通常根据我将在特定列表上执行的操作的时间复杂性,使用一个操作对另一个操作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    |---------------------|---------------------|--------------------|------------|
    |      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    |
    |--------------------------------------|


    arraylist中的operation get(i)比linkedlist更快,因为:array list:列表接口的可调整大小的数组实现linked list:列表和deque接口的双重链接列表实现

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


    ArrayListLinkedList都实现了List interface,其方法和结果几乎相同。但是,它们之间的差异很少,这会使一个比另一个更好,这取决于需求。

    ArrayList与LinkedList

    1)与LinkedList搜索操作相比,Search:ArrayList搜索操作速度相当快。ArrayList中的get(int index)表示O(1)的性能,而LinkedList表示O(n)的性能。

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

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

    Conclusion: LinkedList element deletion is faster compared to
    ArrayList.

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

    3)Inserts Performance:LinkedList加法给O(1)性能,而ArrayListO(n)性能最差。原因与删除说明相同。

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

    hence the memory consumption is high in LinkedList comparatively.

    这些类别之间的相似之处如下:

    • ArrayList和LinkedList都是列表接口的实现。
    • 它们都保持元素插入顺序,这意味着在显示arraylist和linkedlist元素时,结果集的顺序与元素插入列表的顺序相同。
    • 这两个类都是非同步的,可以使用collections.synchronizedList方法显式同步。
    • 这些类返回的iteratorlistIteratorfail-fast(如果在创建迭代器之后的任何时候对list进行了结构修改,除了通过iterator’s自己的remove或add方法之外,迭代器将throw作为ConcurrentModificationException

    何时使用LinkedList,何时使用ArrayList?

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

      Hence if there is a requirement of frequent addition and deletion in application then LinkedList is a best choice.

    • 搜索(get method操作在Arraylist (O(1))中很快,但在LinkedList (O(n))中不快。

      so If there are less add and remove operations and more search operations requirement, ArrayList would be your best bet.


    ArrayList和LinkedList有各自的优缺点。

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

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

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


    1)基础数据结构

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

    2)LinkedList实现deque

    ArrayList和LinkedList的另一个区别是,除了列表接口外,LinkedList还实现了deque接口,它为add()和poll()以及其他几个deque函数提供了先进先出的操作。3)在arraylist中添加元素在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只在数组中存储数据。

    因此,在ArrayList的情况下,内存需求似乎比LinkedList少,除了当Array将内容从一个数组复制到另一个数组时执行重新调整大小操作的情况。

    如果数组足够大,可能会占用大量内存并触发垃圾收集,这会降低响应时间。

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

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

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

    在我看来,在大多数的实际用途中,在Java中使用ARLYLIST超过LIKEDLIST。


    对于arraylist和linkedlist,remove()和insert()都有O(n)的运行时效率。然而,线性处理时间背后的原因来自两个非常不同的原因:

    在arraylist中,您可以访问o(1)中的元素,但实际上,删除或插入某些内容会使其成为o(n),因为需要更改以下所有元素。

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

    这两种方法中哪一种插入和移除速度更快取决于插入和移除的位置。如果我们更接近一开始,LinkedList将更快,因为我们必须经历相对较少的元素。如果我们接近终点,一个数组列表将更快,因为我们在恒定的时间内到达那里,只需要改变它后面剩下的几个元素。当在中间精确地完成时,LinkedList将更快,因为通过n个元素比移动n个值更快。

    好处:虽然没有办法将这两个方法O(1)用于数组列表,但实际上在LinkedList中有一种方法可以做到这一点。假设我们想浏览整个列表,在路上删除和插入元素。通常,您可以从使用linkedlist的每个元素的最开始,还可以"保存"使用迭代器处理的当前元素。在迭代器的帮助下,在LinkedList中工作时,我们可以获得remove()和insert()的O(1)效率。使它成为我所知道的唯一性能优势,LinkedList总是比ArrayList好。


    arraylist扩展了abstractList并实现了列表接口。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),docx1〔5〕为O(n)arraylist.next()为o(1),linkedlist.next()为o(1)arraylist.remove()是O(n),而linkedlist.remove()是O(1)在arraylistiterator.remove()中是o(n),而在linkedList中,iterator.remove()是o(1)


    我在这里看到的一个测试只进行一次。但我注意到,您需要多次运行这些测试,最终它们的时间将收敛。基本上,JVM需要预热。对于我的特定用例,我需要将项目添加/删除到最后一个增长到大约500个项目。在我的测试中,LinkedList的出现速度更快,连接的LinkedList大约进入50000纳秒,ArrayList大约进入90000纳秒。给予或接受。请参见下面的代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    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;
    }

    我什么时候应该使用LinkedList?主要处理堆栈时,或处理缓冲区时。我什么时候应该使用ArrayList?只有在处理索引时,否则可以将哈希表与链接列表一起使用,然后得到:

    哈希表+链接列表

    • 按O(1)键进入,
    • 按O(1)键插入,
    • 按O(1)键移除
    • 在使用版本控制时,有一个技巧可以用o(1)实现removeall/setall

    这似乎是一个很好的解决方案,在大多数情况下,你应该如何知道:hashtable占用了大量的磁盘空间,因此当您需要管理1000000个元素列表时,它可以成为一个重要的东西。这可能发生在服务器实现中,在客户机中很少发生。

    还可以看看红黑树

    • 随机访问日志(n)
    • 插入日志(n),
    • 删除日志(n)