我很想要HashSet.contains(Object)方法在恒定时间内执行。它只是获取一个对象的哈希码,然后在哈希表中查找它。
首先,有人可以确认这是否属实?
第二,如果是真的,是否存在任何冲突的风险,其中两个对象可能具有相同的哈希码,因此HashSet认为它只有两个对象时只有一个?
答案 0 :(得分:59)
它在O(1)
预期时间内运行,就像任何哈希表一样(假设哈希函数正常)。它由HashMap
支持,其中键是Object。
两个对象可能具有相同的哈希码,但HashSet
不会认为它们是相同的,除非这些对象的equals
方法表明它们是相同的(即返回true)。 / p>
contains
方法调用({间接)getEntry
HashMap
Object
,其中密钥为HashSet
,如果它位于HashMap
中,您希望知道该HashSet
如下所示,即使密钥通过哈希函数映射到相同的值,也可以在equals
/ final Entry<K,V> getEntry(Object key) {
int hash = (key == null) ? 0 : hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
中存储两个对象。该方法迭代具有相同散列值的所有键,并在每个键上执行{{1}}以找到匹配的键。
{{1}}
答案 1 :(得分:7)
对于Java 8,contains的最坏情况性能为O(log n),对于Java 7,contains的最坏情况性能为O(n),但平均情况更接近O(1)。这是因为哈希集由哈希图支持,因此具有与哈希图查找(即HashMap.get(...))相同的效率。哈希图中的实际映射是恒定时间(O(1)),但是处理冲突的需要带来了登录n的开销。也就是说,散列到同一数组索引的多个元素必须存储在辅助数据结构(也称为存储桶)中,而这个存储桶决定了最坏情况下的性能。在Java中,哈希映射冲突处理是使用自平衡树实现的。
自平衡树保证所有操作的O(log n),因此,在哈希映射(和哈希集)中进行插入和查找的总成本为O(1)+ O(log n)= O(log n)。在Java 8中引入了使用自平衡树进行冲突处理,这是对链链接(直到Java 7为止使用)的改进,链链接使用链表,并且在查找和插入时最坏的情况是O(n) (因为它需要遍历列表)。注意,链接可以有固定的插入时间(而不是查找),因为可以将元素添加到O(1)的链表中,但是在以下情况下,链表将设置set属性(不重复)哈希图,因此在插入的情况下也需要遍历链表,以确保该元素在列表/存储桶中不存在,并且在插入和查找时都以O(n)结尾。>
参考文献:
此类实现Set接口,并由哈希表支持 (实际上是一个HashMap实例)。 https://docs.oracle.com/javase/8/docs/api/java/util/HashSet.html
包含大量碰撞键的桶将存储它们 某些情况下,平衡树中的条目而不是链表 达到阈值。 (https://www.nagarro.com/en/blog/post/24/performance-improvement-for-hashmap-in-java-8)