【问题标题】:java HashMap collisionjava HashMap 冲突
【发布时间】:2012-12-23 22:53:13
【问题描述】:

我正在阅读有关 hashmap 的工作原理。我正在阅读 "What will happen if two different objects have same hashcode"

根据它,如果两个对象具有相同的哈希码,两者都将存储在LinkedList,但据我所知,如果两个哈希码,那么前一个哈希码将被新的覆盖(如果我错了,请纠正我)。

有人可以详细说明一下 hashmap 如何在内部使用对象作为键,如果两个对象具有相同的 hashcode 会发生什么,以及如何使用get() 获取这两个对象?

【问题讨论】:

标签: java hashmap


【解决方案1】:

不,第一个不会因为第二个具有相同的hashCode 而被覆盖。

只有当它也相等时才会被覆盖(如equals 所说)。如果没有,这两个值都将保存在链表中。

获取键时,所有具有相同hashCode 的节点将与提供的键进行比较,直到一个相等,然后返回其值(使用equals 方法)。

如果地图中没有相同的键,您将获得null

如果许多对象具有相同的 hashCode(或者更确切地说是相同的 hashCode 以内部 Entry[] table 的大小为模),您遇到的唯一问题是链接列表将始终被读取,这更慢(并且违背了目的任何哈希表)。这就是为什么在设计 hashcode 方法以确保生成的整数分布良好时很重要的原因。

【讨论】:

  • 你有任何证据,或者一个例子吗?互联网上散布着诸如“它创建了一个链接列表”之类的声明,但规范根本没有说明这一点。它是如何创建链表的? get 方法应该返回一个对象。
  • A proof ?它散布在互联网上,因为哈希表和哈希映射设计非常古老且标准。我们中的许多人早在 Java 诞生之前就已经使用或编码过哈希映射。
  • 那么你是否需要插入一个条目,因为get 据称返回了一个V 类型的实例,无法链接:docs.oracle.com/javase/7/docs/api/java/util/… There can be at most one such mapping
  • 条目是内部的。您不会直接放置或获取它们。这是在内部为您添加的(包装您的 V 实例)。
  • 两种解决方案:1)使用数组作为值。 2)使用能够为每个键存储多个值的集合,例如番石榴的multimap
【解决方案2】:

让我解释一下 hashmap 的工作原理。

put方法的工作原理:

HashMap 工作原理是散列,我们有put()get() 方法来存储和检索对象形式的HashMap。当我们将键和值都传递给put() 方法以存储在 HashMap 上时,它使用键对象hashcode() 方法来计算哈希码,并且它们通过对该哈希码应用哈希来识别存储值对象的桶位置。在检索它时使用键对象等于方法来找出正确的键值对并返回与该键关联的值对象。 HashMap 在发生冲突时使用链表,对象将存储在链表的下一个节点中。 HashMap 也将 key+value 元组存储在链表的每个节点中

get 方法的工作原理:

当我们将 Key 和 Value 对象传递给 Java HashMap 上的put() 方法时,HashMap 实现会调用 Key 对象上的 hashCode 方法,并将返回的 hashcode 应用到自己的哈希函数中,以找到存储 Entry 对象的桶位置,重要的一点要提一下是Java中的HashMap将key和value对象都存储为桶中的Map.Entry。如果在桶中找到多个 Entry 对象,它将调用同一桶中每个节点的 ke.equals 方法。

【讨论】:

    【解决方案3】:

    假设您遵循defining hashCode and equals 的规则,您描述的场景不会导致数据丢失。最多,性能会随着时间的推移而下降。

    【讨论】:

      【解决方案4】:

      在 Java hashmap 中,他们可以使用多种方法来实现。来自我在黑暗时代的旧 CS 201 数据结构课程:

      1) 哈希图中的每个桶都可以成为链表的头部,该链表包含所有添加的具有相同哈希值的条目。添加时发生冲突意味着您将新条目添加到链表的末尾。搜索意味着,一旦您散列到桶中,您必须线性检查任何链表中的所有链表。

      2) 如果发生冲突并且存储在概念上是一个数组,您可以从该点开始迭代,直到找到一个空点并在那里添加新条目。对于搜索,这意味着如果您发现哈希桶被占用,那么您必须从该点线性比较数组中支持哈希映射的下一个空点。

      在这两种情况下,如果有多个条目具有相同的哈希值,性能就会下降。在一般情况下,这意味着散列函数(用于生成散列码)返回少量可能的值,随着映射的填满,性能会下降。 Java HashMap 利用 50 年来对此类事物的研究,非常适合一般数据进入散列映射的一般情况。

      注意@dystroy 对根据 equals() 方法在映射中不能有两个匹配项的规则发表了评论。

      【讨论】:

      • 请注意,您的数据可能具有一些特征,这些特征使得生成哈希码很重要,该哈希码可以提高数据的性能,但通常会很糟糕。也就是说,如果您需要更好的性能。
      【解决方案5】:

      在 Java 8 中,他们彻底检查了 HashMap 的实现。现在哈希桶被组织为链表或平衡二叉树,具体取决于:

      • 哈希数组大小,和/或
      • 给定存储桶中的条目数。

      这意味着在许多条目位于同一个哈希桶的情况下,您不再会遇到灾难性的糟糕性能。

      欲了解更多信息,请阅读这篇博文:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-11-10
        • 2020-09-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多