java - 何时使用LinkedList ArrayList?

  显示原文与译文双语对照的内容

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


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

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

LinkedList 什么时候应用于 ArrayList 和 vice-versa?

时间:

在更 use-cases 多,比 TL ;DRArrayDeque 是 preferable. ArrayList 不确定—刚从 ArrayList 开始。


LinkedList和ArrayList是两种不同的列表接口实现。 LinkedList用doubly-linked列表实现它。 ArrayList用动态调整大小的数组实现它。

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

对于 LinkedList<E>

  • get(int index) 是 O(n )
  • add(E element) 是 O(1 )
  • add(int index, E element) 是 O(n )
  • remove(int index) 是 O(n )
  • Iterator.remove() 是 O(1) <---的主要优点
  • ListIterator.add(E element) 是 O(1) <---的主要优点

对于 ArrayList<E>

  • get(int index) 是 O(1) <---的主要优点
  • add(E element) 已经被 O(1) 分摊,但由于数组必须调整大小和复制,O(n) 最差
  • add(int index, E element) O(n - index) 已经分摊,但 O(n) 最差( 如上所述)
  • remove(int index) 是 O(n - index) ( 例如 是 O(1) )
  • Iterator.remove() 是 O(n - index )
  • ListIterator.add(E element) 是 O(n - index )

为 constant-time LinkedList<E> 允许插入或者移除操作的使用迭代器 ,但是只有顺序访问的元素。 换句话说,你可以向前或者向后遍历列表,但是在列表中找到一个位置需要与列表的大小成比例。

另一方面,ArrayList<E> 允许快速读取访问,所以你可以在常量时间内抓取任何元素。 但从任何地方添加或者删除,但最后结束需要移位所有后者的元素,无论是要进行通路或者填充空档。 另外,如果你添加的元素多于底层数组的容量,则会分配一个新数组( 1.5倍大小),并将旧数组复制到新数组中,因此在最糟糕的情况下,添加到ArrayList是 O(n) 。

所以根据你要做的操作,你应该选择相应的实现。 对任意种类的列表进行迭代实际上是同样的廉价。 ( 使用一个命名 ArrayList 是技术上更快,但是除非你正在做的事情真的performance-sensitive,你不应该为此操心--他们都是常数) 。

使用一个 LinkedList 会发生当你re-use现有的迭代器的主要好处以插入和删除元素。 这些操作可以通过在本地更改列表来在 O(1) 中完成。 在数组中,本文剩下的部分需要被移动 ( 例如 数组列表。 复制) 。另一边,寻找在一个 LinkedList 意味着以下 O(n), 中的链接而在一个 ArrayList O(1) 中可以通过数学计算求得所需位置和访问。

另外,如果你有大列表,记住内存使用也不同。 LinkedList的每个元素都有更多的开销,因为指向下一个和上一个元素的指针也被存储。 ArrayLists没有这个开销。 然而,不管元素是否被添加,ArrayLists占用的内存都是为容量分配的。

ArrayList的默认初始容量是很小的( 10来自 Java 1.4 - 1.7 ) 。 但是,由于底层实现是一个数组,如果你添加了很多元素,那么数组必须调整大小。 为了避免在你知道要添加大量元素时调整大小的高昂代价,使用更高的初始容量构造 ArrayList 。

值得注意的是,向量也实现了列表接口,并且几乎与ArrayList相同。 区别是向量是同步的,所以它是 thread-safe 。 正因为如此,它的速度比ArrayList慢。 就我所知,大多数Java程序员都避免了矢量的偏爱,因为他们可能会显式地同步,如果他们关心的话。

于N 空references,相关 Thusfar 。没有人似乎已经解决了一般内存占用到的该列表除了人们一致认为一个比一个 ArrayListLinkedList"更多"是的所以我做了一些复杂的数据处理给表上展示两个列表到底有多少花

由于引用是 32或者 64位( 即使是空的时候) 在它们的相对系统上,我在 32和 64位 LinkedListsArrayLists 中包含了 4组数据。

ArrayList 注: 了大小显示的直线,则为修剪列表 - 实际上,底层数组的容量将 ArrayList 一般是大于它的当前元素计数。

注意 2: ( 感谢 BeeOnRope ) 作为现在CompressedOops的默认为 64位 从中间JDK6到往上,下面的数值基本上机器都可以匹配对应的32位 命令,当然,除非你特意将它的关闭。

Graph of LinkedList and ArrayList No. of Elements x Bytes

结果清楚地表明 LinkedListArrayList 多很多,特别是在元素数量非常高的情况下。 如果内存是一个因素,请避开 LinkedLists

我使用的公式,如果我做错了,请告诉我,我会修正的。 '''n 是元素的数目。 注意,由于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)

ArrayList 是你想要的。 LinkedList 几乎总是一个( 性能) Bug 。

为什么 LinkedList 很烂:

  • 它使用大量内存对象,因此影响整个进程的性能。
  • 很多小对象对cache-locality来说都是不好的。
  • 任何索引操作都需要遍历,换句话说,具有 O(n) 性能。 这在源代码中不明显,导致算法 O(n) 比使用 ArrayList 慢。
  • 获得良好的性能很棘手。
  • 即使 Big-O的性能与 ArrayList 相同,也可能会显著降低速度。
  • 在源代码中看到 LinkedList 很棘手,因为它可能是错误的选择。

作为一个在非常大规模的SOA Web服务 上执行了大约十年的操作性能工程的人,我宁愿使用LinkedList优于 ArrayList 。 同时steady-state吞吐量的LinkedList要差,进而可能导致购买更多硬件--数组列表在压力作用下的行为可能导致应用程序在群集中扩展它们的数组在近同步性,对于大数组大小可能导致缺乏响应能力在该应用和宕机的,加压状态下,它是灾难性的行为。

类似地,你可以获得更好的流量在某个应用从默认垃圾收集器吞吐量长存,不过,一旦你的java应用转到 10 GB堆可以清盘锁定行动应用将在一个完整的gc从而导致超时,故障期间 25秒钟在SOA应用程序和打击你的sla如果它发生过于频繁。 尽管CMS收集器占用了更多的资源并且没有达到相同的原始吞吐量,但它还是一个更好的选择,因为它具有更多的可以预测和更小的延迟。

如果你所指的性能是吞吐量,并且你可以忽略延迟,那么ArrayList只是性能更好的选择。 在我的工作经验中,我不能忽略最严重的延迟。

是的,我知道,这是一个古老的问题,但我将用我的两个百分点:

LinkedList是几乎总是一个错误的选择,performance-wise 。 这里有一些非常特殊的算法,在这里调用 LinkedList,但是这些算法非常罕见,而且算法通常会很快地依赖清单中的LinkedList插入和删除元素。

在一个常见的用例中,LinkedList优于 ArrayList: 但是,如果你的目标是性能,那么你也应该考虑使用一个 ArrayBlockingQueue ( 如果你可以提前确定队列大小的上限,并且可以分配所有的内存),或者这个 CircularArrayList实现 。 从 2001 ( 是,是,所以你将需要generify它,但是现在我有可以比较的绩效比率才是什么,用数学的语言文章只是在最近的JVM )


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)

http://leepoint.net/notes-java/algorithms/big-oh/bigoh.html

ArrayLists对于write-once-read-many或者附加程序很好,但在前面或者中间的添加/删除时不好。

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

在LinkedList中编辑/删除比ArrayList快。

由数组支持的ArrayList,它需要两倍的大小,在大容量应用程序中更糟糕。

在下面nanoseconds,是 单元测试 结果对定每个 operation.Timing 都是



 ArrayList Linked List 

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);
 }
 }

这是一个效率问题。LinkedList对于添加和删除元素来说很快,但访问特定元素的速度较慢。 在middle,数组列表是快速,用于访问特定元素,但速度会很慢,可以添加到任何一端,并特别缓慢 delete.

http://www.javafaq.nu/java-article1111.html --的深度更高, http://en.wikipedia.org/wiki/Linked_list

ArrayList是随机访问的,而LinkedList实际上很便宜,可以从。 在大多数情况下,ArrayList很好。

除非你是大型列表和创建有实测的瓶颈,也许永远不需要担心这个的差别。

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

get 很清晰。 O(1)的ArrayList,因为 ArrayList 允许使用索引进行随机访问。 O(n)的LinkedList,因为它需要先查找索引。 注意:addremove 有不同的版本。

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

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

=== 数组列表 == =

  • add(E e )
        • 在ArrayList末尾添加
        • 需要内存调整成本。
        • O(n) 最差,O(1) 已经分摊
  • add(int index, E element )
        • 添加到特定索引位置
        • 需要转移&可能的内存调整成本
        • O(n )
  • remove(int index )
        • 删除指定的元素
        • 需要转移&可能的内存调整成本
        • O(n )
  • remove(Object o )
        • 从此列表中删除指定元素的第一个匹配项
        • 首先需要搜索元素,然后移动&可能的内存调整成本
        • O(n )

=== LinkedList == =

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

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

下面是来自 programcreek.com 。( addremove 是第一个类型,换句话说,在列表的末尾添加一个元素,并在列表的指定位置移除元素。)的图:

enter image description here

...