【问题标题】:Any big difference between using contains or loop through a list?使用包含或循环列表之间有什么大区别吗?
【发布时间】:2011-02-22 13:43:42
【问题描述】:

性能方面,使用之间真的有很大区别吗:

  • ArrayList.contains(o) 与 foreach|iterator
  • LinkedList.contains(o) 与 foreach|iterator

当然,对于 foreach|iterator 循环,我必须明确比较方法并相应地返回 true 或 false。

我要比较的对象是 equals()hashcode() 都被正确覆盖的对象。

编辑:毕竟不需要知道 containsValue,对此感到抱歉。是的,我很愚蠢......我意识到我的问题是关于 containsKey 与 foreach 的愚蠢,别管那个,我不知道我在想什么。我基本上想知道上面的那些(删掉了其他的)。

【问题讨论】:

  • 不用担心 - 这样的问题会更有趣。 :)
  • 使用.contains(o) 与循环的另一个原因是,正如答案中所见,循环的方式取决于集合类型,而 contains() 适用于任何集合。您根本不在乎它是 ArrayList 还是 LinkedList,甚至是 List;但是您仍然坚持使用地图的 .containsKey(k)containsValue(v) 二分法。
  • 对于LinkedListcontains 的速度大约是 for-each 的两倍(参见 stackoverflow.com/questions/2804250/…

标签: java list iterator foreach contains


【解决方案1】:

已编辑:

随着问题的新形式不再包括 HashMap 和 TreeMap,我的答案完全不同。我现在说

我相信其他人已经回答了这个问题,但是在 LinkedList 和 ArrayList 中,contains() 只是调用 indexOf(),它会遍历集合。

LinkedList 和 ArrayList 之间以及 contains 和 foreach 之间可能存在微小的性能差异,但没有任何差异。

【讨论】:

  • HashMap 和 TreeMap 不是列表,除了你可以迭代值或键
  • @stacker,OP 已经编辑了这个问题,使其不再指代 HashMap 或 TreeMap(原来的做法)。当我回到家时,我将编辑我的答案以与问题的当前形式相关。
  • 抱歉修改了 OP,这种情况有时会发生
  • @stacker 是的,它发生了。不用担心。
【解决方案2】:

如果没有基准测试,包含在所有情况下都应该更快或相同。

对于 1 和 2,它不需要调用迭代器方法。它可以在内部循环。 ArrayList 和 LinkedList 都实现了 indexOf

  1. ArrayList - indexOf 是支持数组上的 C 风格 for 循环。
  2. LinkedList - indexOf 以 C 风格的 for 循环遍历链表。

对于 3 和 4,你必须区分 containsKey 和 containsValue。

3。 HashMap, containsKey 为 O(1)。它的工作原理是散列密钥,获取关联的存储桶,然后遍历链表。 containsValue 为 O(n),只需检查嵌套 for 循环中每个存储桶中的每个值即可工作。

4。 TreeMap, containsKey 为 O(log n)。它检查它是否在范围内,然后搜索红黑树。 containsValue,即 O(n),使用树的有序遍历。

【讨论】:

  • 是什么让它更快?我试图查看代码以查看 contains() 是如何实现的,但 Netbeans“导航到源”选项只显示接口而不是实际实现......我虽然 contains() 有点像循环并调用 equals但我决定问...
  • 它不必初始化 Iterator 实例,因为它已经可以访问内部的值。是的,这正是它的工作原理(见我的回答)。 (嗯,实际上对于 ArrayList 你可以使用一个 int 计数器,而 l.get(i) 只是从数组中获取索引 i 处的值,所以如果你这样做不会有太多开销)。
【解决方案3】:

ArrayList.contains 确实

return indexOf(o) >= 0;

在哪里

public int indexOf(Object o) {
if (o == null) {
    for (int i = 0; i < size; i++)
    if (elementData[i]==null)
        return i;
} else {
    for (int i = 0; i < size; i++)
    if (o.equals(elementData[i]))
        return i;
}
return -1;
}

LinkedList 类似,只是它使用 .next() 来遍历元素,所以没有太大区别。

public int indexOf(Object o) {
    int index = 0;
    if (o==null) {
        for (Entry e = header.next; e != header; e = e.next) {
            if (e.element==null)
                return index;
            index++;
        }
    } else {
        for (Entry e = header.next; e != header; e = e.next) {
            if (o.equals(e.element))
                return index;
            index++;
        }
    }
    return -1;
}

HashMap.containKey 使用键的散列来获取具有该散列的所有键(速度很快),然后仅在这些键上使用等于,所以那里有一个改进;但是 containsValue() 使用 for 遍历值。

TreeMap.containsKey 似乎使用比较器进行知情搜索以更快地找到密钥,因此更好;但是 containsValue 似乎仍然会遍历整个三个,直到找到一个值。

总的来说,我认为你应该使用这些方法,因为它们比每次都循环更容易编写:)。

【讨论】:

    【解决方案4】:

    这没有区别,因为 contains(o) 调用 indexOf(o),它只是像这样循环:

    for (int i = 0; i < size; i++)
        if (o.equals(elementData[i]))
           return i;
    

    (已在 ArrayList 中检查)

    【讨论】:

    • +1 好吧,不同之处在于 contains() 有效,而我偶尔会看到程序员遇到简单的循环错误(经典的 off-by-1 错误 ;-))。
    • @Helper 好点。 OP 的问题是关于性能的,但值得强调的是清晰的价值。
    【解决方案5】:

    使用 foreach/iterator 遍历容器总是 O(n) 时间。 ArrayList/LinkedList 搜索也是 O(n)。

    HashMap.containsKey() 是 O(1) amortized 时间。

    TreeMap.containsKey() 是 O(log n) 时间。

    HashMap 和 TreeMap 的 containsValue() 都是 O(n),但可能有针对 containsValue() 优化的实现与 containsKey() 一样快。

    【讨论】:

      【解决方案6】:

      我认为使用 contains 更好,因为通常库实现比手动实现更有效。检查您是否可以在对象构造期间或之后传递您编写的比较器方法,该方法负责您的自定义等号和哈希码实现。

      谢谢, 克里希纳

      【讨论】:

        猜你喜欢
        • 2021-11-14
        • 1970-01-01
        • 2019-02-12
        • 1970-01-01
        • 2016-04-29
        • 2011-04-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多