【问题标题】:Using a LinkedList or ArrayList for iteration使用 LinkedList 或 ArrayList 进行迭代
【发布时间】:2012-01-16 05:08:22
【问题描述】:

如果我要向列表添加未知数量的元素,并且该列表只会被迭代,那么在特定实例中 LinkedList 是否会比 ArrayList 更好(使用 Java,如果有任何相关性)

【问题讨论】:

  • 我正在添加一些对列表的引用。然后将该列表传递给另一个对象,迭代一次,然后丢弃。从 Oscars 链接所指向的内容来看,LinkedList 似乎是更好的选择。
  • stackoverflow 不是找到答案的好地方。使用两者,进行基准测试并得出答案。这样的事情可能会有所帮助:javaspecialists.eu/archive/Issue111.html
  • 这是一个很好的问题。但是,您可以通过自己的基准帮助我们。 +1

标签: data-structures arraylist linked-list


【解决方案1】:
public List<Integer> generateArrayList(int n) {
    long start = System.nanoTime();
    List<Integer> result = new ArrayList<>();
    for (int i = 0; i < n; i++) {
        result.add(i);
    }
    System.out.println("generateArrayList time: " + (System.nanoTime() - start));
    return result;
}

public List<Integer> generateLinkedList(int n) {
    long start = System.nanoTime();
    List<Integer> result = new LinkedList<>();
    for (int i = 0; i < n; i++) {
        result.add(i);
    }
    System.out.println("generateLinkedList time: " + (System.nanoTime() - start));
    return result;
}

public void iteratorAndRemove(List<Integer> list) {
    String type = list instanceof ArrayList ? "ArrayList" : "LinkedList";
    long start = System.nanoTime();
    Iterator<Integer> ite = list.iterator();
    while (ite.hasNext()) {
        int getDataToDo = ite.next();
        ite.remove();
    }
    System.out.println("iteratorAndRemove with " + type + " time: " + (System.nanoTime() - start));
}

@org.junit.Test
public void benchMark() {
    final int n = 500_000;
    List<Integer> arr = generateArrayList(n);
    List<Integer> linked = generateLinkedList(n);
    iteratorAndRemove(linked);
    iteratorAndRemove(arr);

}

Arraylist 用于获取随机位置值,linkedlist 用于插入、删除操作。上面的代码将显示linkedlist比ArrayList快得多,在删除函数linkedlist比arraylist快1000倍,OMG!!!

generateArrayList time: 15997000
generateLinkedList time: 15912000
iteratorAndRemove with LinkedList time: 14188500
iteratorAndRemove with ArrayList time: 13558249400

【讨论】:

  • 当然,没有理智的开发人员会以这种方式从ArrayList 中删除元素。要删除所有元素,只需调用clear(),要删除所有满足条件的元素,请使用removeIf(element -&gt; condition)
【解决方案2】:

好的,已经回答了,但我仍然会尝试表达我的观点。 ArrayList 的迭代速度比 LinkedList 快。原因是一样的,因为arrayList 由数组支持。让我们尝试了解为什么数组迭代比linkedList 更快。

有两个因素起作用

  • 数组存储为contiguous memory locations(你可以这么说
    什么?)
  • 系统缓存比访问内存快得多

但是你可以问一下 Cache 如何适合这里。好吧检查here,CPU 试图通过将数据存储在缓存中来利用缓存。它使用Locality of refrence。现在有两种技术是

参考Locality of refrence

时间局部性 如果在某一点上引用了一个特定的内存位置,那么很可能在 不远的将来。相邻之间存在时间上的接近 引用相同的内存位置。在这种情况下,常见的是 努力将引用数据的副本存储在特殊内存中 存储,可以更快地访问。时间局部性是一个特殊的 空间局部性的情况,即当预期位置是 与当前位置相同。

空间位置 如果在特定时间引用了特定的存储位置,那么附近的内存位置很可能会被 在不久的将来被引用。在这种情况下,通常会尝试 猜测当前参考周围区域的大小和形状 值得为更快的访问做准备。

因此,如果一次访问一个数组位置,它也会将相邻的内存位置加载到缓存中。但是等待它不会全部加载。这取决于CACHE_LINES。那么CACHE_LINES 定义一次可以在缓存中加载多少位。

所以在进一步潜水之前,以免提醒我们所知道的

  • 数组是contiguous memory locations
  • 当数组的一个内存位置被访问时,相邻的也会加载到内存中
  • 在内存中加载多少数组内存位置由CACHE-LINES容量定义

所以每当 CPU 尝试访问内存位置时,它都会检查该内存是否已经在缓存中。如果它存在,则匹配它的cache miss

因此,据我们所知,在数组的情况下,cache_miss 的数量将少于 random memory locations 中的 linked list。所以有道理

最后从这里Array_data_structure from Wikipedia 它说

在元素大小为 k 的数组中和具有高速缓存行的机器上 B 字节的大小,遍历 n 个元素的数组需要 最低上限(nk/B)缓存未命中,因为它的元素占用 连续的内存位置。这大约是 B/k 更好的一个因素 比随机访问 n 个元素所需的缓存未命中次数 内存位置。因此,对数组的顺序迭代 在实践中明显快于对许多其他数据的迭代 结构,一种称为局部性的属性

我想这回答了你的问题。

【讨论】:

    【解决方案3】:

    ArrayListLinkedList 之间的性能权衡已经讨论过before,但简而言之:ArrayList 在大多数实际使用场景中往往更快。 ArrayList 会导致更少的内存碎片,并且会更好地与垃圾收集器一起使用,它会占用更少的内存并允许更快的迭代,并且对于发生在列表末尾的插入会更快。

    因此,只要列表中的插入始终出现在最后一个位置,就没有理由选择 LinkedList - ArrayList 是明显的赢家。

    【讨论】:

      【解决方案4】:

      对于迭代而言,两者在迭代时将具有相同的 O(n) 复杂度,ArrayList 将占用更少的内存 BTW。

      【讨论】:

      • 您能否解释一下在这种情况下对于不了解大 O 表示法的人来说 O(n) 复杂度意味着什么?
      • @Solace 在这种情况下,这意味着集合中元素的数量与迭代集合所需的时间之间存在线性关系。例如,如果您有一个包含 10 个元素的列表和一个包含 20 个元素的列表,您会期望包含 20 个元素的列表需要两倍的迭代时间。这个例子可能看起来很明显,但并非所有内容都以线性方式扩展,如果我有一个列表并且我想遍历每对可能的元素,它将具有 O(n^2) 复杂度。 IE。所花费的时间与您平方的元素数量成正比。
      • 我找不到关于该主题的任何其他参考资料,但不是迭代 LinkedList O(n) 和迭代 ArrayList O(1)? LinkedList返回的迭代器调用了get方法,也就是O(n),而ArrayList返回的迭代器只是从一个数组中获取值,O(1)操作?
      • 不,它也是 O(n),LinkedList 的迭代器包含指向当前和先前元素的链接。它不能比 O(n) 更好,因为要迭代 n 个元素,我们至少需要访问它们一次 - 这是 O(n)。渐近地,它们都对 get() 起作用,但 LinkedList 中的 remove() 操作是 O(1) 与 ArrayList 中的 O(n)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-09-18
      • 2012-03-24
      • 1970-01-01
      • 1970-01-01
      • 2021-11-12
      • 1970-01-01
      • 2015-08-09
      相关资源
      最近更新 更多