When to use LinkedList over ArrayList in Java?
我一直是一个简单的使用:
1 | List<String> names = new ArrayList<>(); |
我使用接口作为可移植性的类型名,这样当我问这样的问题时,我可以重新编写代码。
何时应将
总结:在更多的用例中,使用
与标准的链表和数组操作一样,各种方法将具有不同的算法运行时。好的。
用于
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步(列表中间)好的。
对于
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个步骤,最佳情况下的步骤数不变(列表结尾),最差情况下的步骤数不变(列表开头)。好的。
另一方面,
因此,根据您打算执行的操作,您应该相应地选择实现。遍历任何一种列表实际上都是同样便宜的。(在
当您重新使用现有的迭代器来插入和删除元素时,使用
使用
另外,如果您有大的列表,请记住内存使用也不同。
EDCOX1×0的默认初始容量非常小(从Java 1.4到1.8的10)。但是,由于底层实现是一个数组,如果添加了大量元素,则必须调整数组的大小。为了避免在您知道要添加大量元素时调整大小的高成本,请使用更高的初始容量构建
到目前为止,除了普遍认为
由于引用在它们的相关系统上是32位或64位(即使为空),所以我为32位和64位
注:
注2:(感谢BeeonRope)由于compressedoops现在是JDK6和更高版本的默认值,64位机器下面的值基本上与32位机器匹配,除非您特别关闭它。
结果表明,
我使用的公式如下,如果我做错了,请告诉我,我会解决的。"对于32位或64位系统,"b"是4或8,而"n"是元素数。注意MODS的原因是因为Java中的所有对象将占用8字节空间的倍数,而不管它是否被全部使用。
ArrayList:
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中引用的性能比率相比较的结果)
这是一个效率问题。
array vs arraylist vs linkedlist vs vector更深入链表。
正确或不正确:请在本地执行测试并自行决定!
由
下面是每个操作的单元测试结果。计时以纳秒为单位。
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); } } |
===数组列表===
- 添加(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的一个图(
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
我很抱歉这个答案没有其他答案那么有信息量,但我认为这将是最有趣和最不言自明的。
除非您已经创建了大量的列表并测量了瓶颈,否则您可能永远不需要担心差异。
如果您的代码有
当然,瓜娃的不变清单是你最好的朋友。
我知道这是一个古老的职位,但我真的不敢相信没有人提到
以下是
数组列表
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).
来源
由于现代计算机体系结构的原因,
理论上,LinkedList对于
另外,在列表中间添加元素应该是非常有效的。
实践是非常不同的,因为LinkedList是一个缓存敌对的数据结构。从性能POV来看,很少有情况下,
下面是在随机位置插入元素的基准测试的结果。如您所见-数组列表如果效率更高,尽管理论上,列表中间的每个插入都需要"移动"数组后面的n个元素(值越低越好):
在新一代硬件上工作(更大、更高效的缓存)-结果更具决定性:
LinkedList需要更多的时间来完成相同的工作。源代码
这主要有两个原因:
主要是-EDOCX1的节点(0)随机分布在内存中。RAM("随机存取存储器")不是真正随机的,需要将内存块提取到缓存中。此操作需要花费时间,并且当此类提取频繁发生时,需要始终替换缓存中的内存页->缓存未命中->缓存无效。
二级
动态tarray,btw,是一个自定义的arraylist实现,包含
要记住的一个关键要素是,获取内存块的成本比访问单个内存单元的成本更重要。这就是为什么读卡器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 |
来源:每个程序员应该知道的延迟数
为了更清楚地说明这一点,请检查在列表开头添加元素的基准。这是一个用例,在理论上,
注:这是C++ STD LIB的一个基准,但是我以前的经验表明C++和Java的结果非常相似。源代码
复制一个连续的大容量内存是一个由现代CPU变化理论优化的操作,实际上使
学分:这里发布的所有基准都是由Kjell Hedstr创建的?M.在他的博客上可以找到更多的数据。
除了上面的其他好论点,您应该注意到
所以,不知何故,他们解决的问题略有不同,效率和行为也有所不同(见他们的方法列表)。
请参阅Java教程-列表实现。
数组列表本质上是一个带有添加项等方法的数组(您应该使用一个通用列表)。它是可以通过索引器(例如[0])访问的项的集合。它意味着从一个项目到下一个项目的进展。
链接列表指定从一个项目到下一个项目(项目A->项目B)的进度。您可以使用数组列表获得相同的效果,但链接列表绝对会说明应该遵循上一个列表的项目。
链接列表的一个重要特性(我没有在另一个答案中读到)是两个列表的串联。对于数组,这是o(n)(+某些重新分配的开销),对于链接列表,这只是o(1)或o(2);-)
重要的是:对于Java,它的EDCOX1(0)这是不正确的!在Java中有一个链表的快速CONTAT方法吗?
这取决于您将在列表中做更多的操作。
要了解更多信息,请阅读任何讨论数组和链接列表之间区别的文章。
我已经阅读了回复,但有一种情况是,我总是使用一个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接口的双重链接列表实现
索引到列表中的操作将从开始或结束遍历列表,以更接近指定索引的为准。
1)与
2)
Conclusion: LinkedList element deletion is faster compared to
ArrayList.
原因:LinkedList的每个元素维护两个指向列表中两个相邻元素的指针(地址)。因此,删除只需要更改要删除的节点的两个相邻节点(元素)中的指针位置。在arraylist中,需要移动所有元素以填充被删除元素创建的空间。
3)
4)
hence the memory consumption is high in LinkedList comparatively.
这些类别之间的相似之处如下:
- ArrayList和LinkedList都是列表接口的实现。
- 它们都保持元素插入顺序,这意味着在显示arraylist和linkedlist元素时,结果集的顺序与元素插入列表的顺序相同。
- 这两个类都是非同步的,可以使用collections.synchronizedList方法显式同步。
- 这些类返回的
iterator 和listIterator 是fail-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接口。性能
我在这里看到的一个测试只进行一次。但我注意到,您需要多次运行这些测试,最终它们的时间将收敛。基本上,JVM需要预热。对于我的特定用例,我需要将项目添加/删除到最后一个增长到大约500个项目。在我的测试中,
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; } |
我什么时候应该使用
- 按O(1)键进入,
- 按O(1)键插入,
- 按O(1)键移除
- 在使用版本控制时,有一个技巧可以用o(1)实现removeall/setall
这似乎是一个很好的解决方案,在大多数情况下,你应该如何知道:hashtable占用了大量的磁盘空间,因此当您需要管理1000000个元素列表时,它可以成为一个重要的东西。这可能发生在服务器实现中,在客户机中很少发生。
还可以看看红黑树
- 随机访问日志(n)
- 插入日志(n),
- 删除日志(n)