【问题标题】:How HashMap really works?HashMap 是如何工作的?
【发布时间】:2015-08-22 17:53:47
【问题描述】:

根据这个问题

how-does-a-hashmap-work-in-javathis

许多键值对可以存储在同一个桶中(在使用哈希计算桶的索引之后),当我们调用get(key)时,它会查看链表并使用equals方法进行测试。

对我来说听起来并没有真正优化,不是在使用equals之前比较链接列表的hashCodes吗?

如果答案是否定的:

这意味着大部分时间桶只包含 1 个节点,你能解释一下为什么吗?因为根据this logical explanation,许多不同的键可能具有相同的桶索引。

如何实现密钥的良好分配?这可能意味着桶表的大小与键的数量有关

即使Table Bucket的大小等于key的个数,HashMaphashCode函数如何保证key的良好分布?不是随机分布吗?,

我们可以提供更多详细信息吗?

【问题讨论】:

  • 这是由他在链接问题中的第一个答案回答的。
  • 不,它说:hashmap 然后会查看相应的存储桶,然后它将您提供的密钥与存储桶中所有对的密钥进行比较,方法是将它们与 equals() 进行比较。
  • 哪个回答了您的问题“它是否比较链表的哈希码而不是等于?”。 (即“不”)
  • 但它没有优化,许多元素可以共享同一个桶,我更新我的问题
  • 通常这不会发生,除非你的对象的hashCode 没有很好地实现,所以最后一个简单的put 操作将花费O(N),但这不是@ 的问题987654331@ 实现,在key的hashCode中。

标签: java


【解决方案1】:

该实现是开源的,因此我鼓励您仅read the code 解决任何具体问题。但总体思路如下:

  • 良好的 hashCode 分布的主要责任在于 keys' 类,而不是 HashMap。 如果键的 hashCode() 方法分布不好(例如,@ 987654325@) 那么 HashMap 的性能就会很差。
  • HashMap 确实做了一些“重新散列”以确保更好的分布,但不多(参见HashMap::hash
  • get 方面,对存储桶中的每个元素进行了几次检查(是的,它被实现为链表)
    • 首先,HashMap 使用传入键的 hashCode 检查元素的 hashCode。这是因为这个操作很快,并且元素的 hashCode 在put 时间被缓存。这可以防止具有不同 hashCode 的元素(因此根据 hashCode 的约定和 Object 建立的 equals 不相等)但恰好落入同一个桶中(请记住,桶索引基本上是 hashCode % buckets.length
    • 如果成功,那么,HashMap 会明确检查equals 以确保它们真的相等。请记住,相等意味着相同的 hashCode,但相同的 hashCode 需要相等(并且不能,因为某些类可能具有无限数量的不同值 - 例如String - 但只有一个可能的 hashCode 值的数量有限)

hashCode 和 equals 双重检查的原因是既快速又正确。考虑两个具有不同 hashCode 但最终位于同一个 HashMap 存储桶中的键。例如,如果密钥 A 的 hashCode=7 且 B 的 hashCode=14,并且有 7 个桶,那么它们最终都会在桶 0 中(7 % 7 == 014 % 7 == 0)。检查 hashCodes 有一种快速的方法可以看到 A 和 B 不相等。如果您发现 hashCode 相等,那么您可以通过调用 equals 来确保这不仅仅是 hashCode 冲突。这只是一个优化,真的;一般哈希映射算法不需要。

【讨论】:

  • 所以我的直觉很好?它在链接列表查看期间比较 hashCode 而不是使用 Equals?
  • 不,它比较两者。首先它比较 hashCode 作为一种快速检查以排除大多数误报,然后它使用 equals 来获得真正的答案。
  • 是的,我知道它会在后面使用equal,但这意味着我的直觉很好,而第一篇文章是错误的?
  • 我不确定您所说的第一篇文章是指哪个帖子。
  • 那么是的,如果他们专门讨论 JDK 的 HashMap 实现(在我见过的任何 JDK 中——Oracle 的 Java 6、7 和 8 中),那么他们是不正确的。如果他们一般谈论“哈希映射”的概念,作为一种数据结构,那么这些映射真正需要检查相等性 - hashCode 检查是一个细节/优化。跨度>
【解决方案2】:

为了避免在链表中进行多重比较,HashMap 中的桶数通常保持足够大,以使大多数桶只包含一项。默认情况下,java.util.HashMap 会尝试维护足够的桶,以使项目数仅为桶数的 75%。

一些桶可能仍然包含多个项目 - 所谓的“哈希冲突” - 而其他桶将是空的。但平均而言,大多数包含项目的存储桶仅包含一项。

equals() 方法将始终至少使用一次来确定键是否完全匹配。请注意,equals() 方法通常至少与 hashCode() 方法一样快。

良好的键分布由良好的 hashCode() 实现维护; HashMap 对此影响不大。一个好的 hashCode() 方法是返回的哈希与对象的值尽可能随机的关系。

举一个糟糕的散列函数的例子,曾几何时,String.hashCode() 方法只依赖于字符串的开头。问题在于,有时您想在 HashMap 中存储一串以相同开头的字符串 - 例如,单个网站上所有页面的 URL - 导致哈希冲突比例过高。我相信 String.hashCode() 后来被修改来解决这个问题。

【讨论】:

  • 好的,但对我来说仍然是随机分布
【解决方案3】:

它不是比较链表的哈希码而不是使用 等于

它不是必需的。 hashcode 用于确定存储桶的编号,无论是 put 还是 get 操作。一旦您知道带有哈希码的存储桶编号并在那里找到它的链表,那么您就知道您需要对其进行迭代并需要检查相等性以找到确切的 key 。所以这里不需要hashcode比较

这就是为什么哈希码应该尽可能独特,以便最好地查找。

这意味着大多数时候bucket只包含1个节点

没有。这取决于hascode的唯一性。如果两个 key 对象的 hashcode 相同但不相等,则桶中包含两个节点

【讨论】:

  • 查看我的扩展答案
  • 根据javamadesoeasy.com/2015/02/hashmap-custom-implementation.html许多不同的键可能有相同的桶索引,它不回答这个问题
  • @NassimMOUALEK:这可能是因为不清楚问题实际上是什么...请仔细阅读en.wikipedia.org/wiki/Hash_table,如果确定,然后返回具体/有针对性的问题事情仍然没有意义。
  • 我只是试图准确地理解 hashMap 是如何实现的,对我来说,hashmap 用 Equal 来查看存储桶是不合逻辑的,前提是它确保其中有最少数量的元素,问题是如何知道桶表的大小以提供良好的分布
【解决方案4】:

当我们将 Key 和 Value 对象传递给 Java HashMap 上的 put() 方法时,HashMap 实现会调用 Key 对象上的 hashCode 方法,并将返回的 hashcode 应用到自己的哈希函数中,以找到存储 Entry 对象的桶位置,重点提一下是Java中的HashMap将key和value对象都存储为Map.Entry在bucket中,这对于理解检索逻辑至关重要。

在检索某个Key的Values时,如果hashcode与其他一些key相同,则bucket位置相同,HashMap中会发生冲突,由于HashMap使用LinkedList来存储对象,因此该条目(Map.Entry的对象)包含key和 value ) 将存储在 LinkedList 中。

密钥的良好分配将取决于hashcode 方法的实现。此实现必须遵守哈希码的一般约定:

  1. 如果两个对象通过 equals() 方法相等,则 hashCode() 方法返回的哈希码必须相同。
  2. 只要在应用程序的单次执行中多次对同一对象调用 hashCode() 方法,hashCode() 必须返回相同的整数,前提是未在 equals 中使用任何信息或字段并且修改了 hashcode。但是,在应用程序的多次执行期间,此整数不需要相同。
  3. 如果两个对象通过 equals() 方法不相等,则不需要哈希码必须不同。尽管为不相等的对象返回不同的 hashCode 总是好的做法。不同对象的不同 hashCode 可以通过减少冲突来提高 hashmap 或 hashtable 的性能。

【讨论】:

    【解决方案5】:

    您可以访问这个 git-hub 存储库“https://github.com/devashish234073/alternate-hash-map-implementation-Java/blob/master/README.md”。

    您可以通过基本实现和示例了解 HashMap 的工作原理。 ReadMe.md 解释了一切。

    在此处包含示例的某些部分:

    假设我必须存储以下键值对。 (key1,val1) (key2,val2) (key3,val3) (……,……) (key99999,val99999)

    让我们的哈希算法只产生 0 到 5 之间的值。

    所以首先我们创建一个带有 6 个桶的机架,编号为 0 到 5。


    存储:

    存储 (keyN,valN):
    1.获取'keyN'的哈希值
    2.假设我们得到 2
    3.将 (keyN,valN) 存储在机架 2 中

    搜索:

    搜索keyN:
    1.获取keyN的哈希值
    2.让我们说我们得到 2
    3.遍历机架2,获取key并返回值

    因此,对于 N 个键,如果我们要线性存储它们,将需要 N 个比较来搜索最后一个元素,但是对于哈希算法生成 25 个值的 hashmap,我们只需要进行 N/25 个比较。 [散列值均匀分布]

    【讨论】:

    • 你必须放一段代码并从中解释。这不能回答问题,您应该将其作为评论。但是,这个问题应该是题外话。
    猜你喜欢
    • 1970-01-01
    • 2015-06-10
    • 1970-01-01
    • 1970-01-01
    • 2014-09-29
    • 1970-01-01
    • 2015-08-22
    • 2012-06-08
    • 2013-03-12
    相关资源
    最近更新 更多