如果两个不同的对象具有相同的哈希码会发生啥?

Posted

技术标签:

【中文标题】如果两个不同的对象具有相同的哈希码会发生啥?【英文标题】:What happens if two different objects have the same hashcode?如果两个不同的对象具有相同的哈希码会发生什么? 【发布时间】:2012-06-27 01:04:06 【问题描述】:

我的理解是两个不相等的对象可以有相同的哈希码。从 HashMap java 添加或检索时如何处理?

【问题讨论】:

顺便说一句:您可以轻松地创建许多具有相同哈希码的 Long 值来尝试此操作。 new Long(n * 0x100000001L) 对于 n >= 0 的 hashCode 都为 0 【参考方案1】:

HashMap 正在研究散列和索引的概念。 内部 HashMap 将值存储在节点数组中。 每个节点都表现为 LinkedList。

链表的每个节点都有4个值:

    int hash K key V value Node<K, V> next

HashMap 内部结构:

在 HashMap 中插入值时,会生成 Key 的第一个 hashcode,并根据某种算法计算索引。

所以我们的值将与下一个元素的哈希码、键、值和地址一起存储在特定的索引中。

从 HashMap 中检索值时,首先生成哈希码,然后生成索引(与插入时的方式相同)。在从索引中获取值时,首先它会检查哈希码,如果哈希码匹配,那么它只会使用 equals 方法从 Node 中检查键。 如果键匹配,那么只有它会返回值,否则它将检查具有相同哈希码的下一个节点。

【讨论】:

【参考方案2】:

当两个不相等的对象具有相同的哈希值时,这会导致哈希表中的冲突,因为两个对象都希望位于同一个槽(有时称为存储桶)中。哈希算法必须解决此类冲突。回到我大学算法课程的褪色记忆中,我记得这样做的三种基本方法:

    在哈希表中寻找下一个空槽并将对象放在那里。优点:易于实施,缺点:可能导致对象聚集并降低性能,可能会超出容量 在发生冲突时使用辅助散列函数:优点:通常速度快,缺点:必须编写第二个散列函数并且可能仍然会发生冲突,并且可能会超出容量 从哈希表的冲突槽中创建对象的链接列表。优点/缺点:通常对于不错的散列函数和负载因子来说很快,但在最坏的情况下会降级为线性搜索

我认为 Java 哈希类使用第三种方法,但它们可能使用组合方法。但是,良好散列的关键是确保散列表具有足够大的容量并编写良好的散列函数。一个只有与其持有的对象一样多的桶的哈希表可能会发生冲突。通常你希望哈希表大约是它存储的对象数量的两倍。 Java 的 HashMap 会根据需要增长,但如果你愿意,你可以给它一个起始容量和负载因子。

哈希函数由程序员决定。您可以只为所有对象返回 0,但这意味着散列(存储和检索)将变为 O(n) 而不是 O(1) ...或者用通俗的话来说,它会很慢。

参考:http://www.coderanch.com/t/540275/java/java/objects-hashcode-HashMap-retrieve-objects

【讨论】:

【参考方案3】:

在这种情况下,您可以使用 IdentityHashMap,其中具有相同哈希的不同对象根据其身份被视为不同。

【讨论】:

【参考方案4】:

在 HashMap 中,键及其关联值存储在桶中的链表节点中,并且键在 hashmap 中本质上是使用 equals() 方法而不是通过哈希码进行比较。

hm.put("a","aValue"); // Suppose hashcode created for key "a" is 209 
hm.put("b","bValue"); // Here hashcode created for key "b" is 209 as well.
如果a.equals(b) 返回truebValue 将替换aValue 并返回bValue。 如果a.equals(b)返回false,则会在桶列表中创建另一个节点,所以当你调用get("b")时你会得到bValue,因为a.equals(b)false

【讨论】:

如果哈希码相同,我如何检索 a 的值?它会给我 bValue,但我想要 aValue。这可能吗? @Sanket,如果 a.equals(b) 为真,则表示键 a、b 相等。所以即使它们具有相同的哈希码,由于键是相等的,旧的(aValue)值将被新的(bValue)值替换。所以你不能检索aValue。希望它有助于为将来有相同问题的人消除困惑。【参考方案5】:

它们只会被添加到同一个存储桶中,equals() 将用于区分它们。 每个桶可以包含一个具有相同哈希码的对象列表。

理论上,您可以为给定类的任何对象返回与哈希码相同的整数,但这意味着您失去了哈希映射的所有性能优势,并且实际上会将对象存储在列表中。

【讨论】:

不是默认情况下对 Hashmap 应用补充哈希以防止这种情况发生而引入一些分布吗? 关于性能的附加点,在 java8 中,当我们有太多不相等的键提供相同的哈希码(索引)时 - 然后哈希桶中的项目数增长超过某个阈值(TREEIFY_THRESHOLD = 8),该存储桶的内容从使用条目对象的链接列表切换到平衡树。这在理论上将最坏情况的性能从 O(n) 提高到 O(log n)。

以上是关于如果两个不同的对象具有相同的哈希码会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章

equals方法比较的是两个对象的哈希码,这么说对吗?

equals方法比较的是两个对象的哈希码,这么说对吗?

如果两个对象的哈希码相同则他们不一定相同,如果对象一致则哈希码一定相同

当对象 Hashcode 更改时,Hashmap 或 Hashset 中的查找会发生啥

Django-检查两个密码哈希是否具有相同的原始密码

为啥我在具有不同哈希的两个分支中有相同的提交