使用包含或循环列表之间有啥大区别吗?
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)
二分法。
对于LinkedList
,contains
的速度大约是 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 更好,因为通常库实现比手动实现更有效。检查您是否可以在对象构造期间或之后传递您编写的比较器方法,该方法负责您的自定义等号和哈希码实现。
谢谢, 克里希纳
【讨论】:
以上是关于使用包含或循环列表之间有啥大区别吗?的主要内容,如果未能解决你的问题,请参考以下文章