使用包含或循环列表之间有啥大区别吗?

Posted

技术标签:

【中文标题】使用包含或循环列表之间有啥大区别吗?【英文标题】: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 的两倍(参见 ***.com/questions/2804250/…) 【参考方案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

    ArrayList - indexOf 是支持数组上的 C 风格 for 循环。 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 更好,因为通常库实现比手动实现更有效。检查您是否可以在对象构造期间或之后传递您编写的比较器方法,该方法负责您的自定义等号和哈希码实现。

谢谢, 克里希纳

【讨论】:

以上是关于使用包含或循环列表之间有啥大区别吗?的主要内容,如果未能解决你的问题,请参考以下文章

在循环内部或外部使用 unsafe 有啥区别吗?

Python中的集合和列表有啥区别?

`xtratim` 和覆盖 p3 之间有啥区别吗?

多方法和多调度之间有啥区别吗?

haskell中的循环列表和无限列表有啥区别?

TouchEvent.Touch 和 MouseEvent.Click 之间有啥技术或处理上的区别吗?