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

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

我使用接口作为可移植性的类型名称,这样当我问类似这样的问题时,我就可以修改代码。

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


当前回答

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

LinkedList在性能方面几乎总是错误的选择。有一些非常具体的算法需要LinkedList,但这些算法非常非常罕见,并且该算法通常具体取决于LinkedLists在使用ListIterator导航到列表中间后相对快速地插入和删除元素的能力。

有一个常见的用例LinkedList优于ArrayList:队列。但是,如果您的目标是性能,那么您也应该考虑使用ArrayBlockingQueue(如果您可以提前确定队列大小的上限,并且能够提前分配所有内存),而不是LinkedList,或者使用CircularArray实现。(是的,它来自2001年,因此您需要对其进行一般化,但我得到的性能比与最近一篇JVM文章中引用的性能比相当)

其他回答

我在这里看到的一个测试只进行一次测试。但我注意到的是,您需要多次运行这些测试,最终它们的时间会收敛。基本上JVM需要预热。对于我的特定用例,我需要向列表中添加/删除项目,该列表将增加到大约500个项目。在我的测试中,LinkedList的发布速度更快,LinkedList约为50000 NS,ArrayList约为90000NS。。。给予或索取。请参见下面的代码。

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

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

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

因此,他们解决的问题略有不同,效率和行为有所不同(见他们的方法列表)。

我的经验法则是,如果我需要一个集合(即不需要是一个列表),那么如果你事先知道大小,或者可以自信地知道大小,或知道它不会有太大变化,那么就使用ArrayList。如果您需要随机访问(即使用get(index)),请避免LinkedList。基本上,只有当您不需要索引访问并且不知道正在分配的集合的(近似)大小时,才使用LinkedList。此外,如果您要进行大量添加和删除(再次通过Collection接口),则LinkedList可能更可取。

1) 基础数据结构

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

2) LinkedList实现Deque

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

4) 从位置移除元素

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

5) 遍历ArrayList或LinkedList

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

6) 从位置检索元素

get(index)操作在ArrayList中为O(1),而在LinkedList中为其O(n/2),因为它需要遍历该条目。虽然,在大O符号中,O(n/2)只是O(n),因为我们忽略了那里的常数。

7) 内存

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

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

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

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

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

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

在我看来,在Java中,使用ArrayList而不是LinkedList来实现大多数实际用途。