Java HashMap是如何工作的?

Java HashMap是如何工作的?,java,Java,根据这个问题, 及 许多键值对可以存储在同一个bucket中(在使用散列计算bucket的索引之后),当我们调用get(Key)时,它会查看链表并使用equals方法进行测试 对我来说,它听起来并没有真正优化过,在使用equals之前,它不是比较了链表的hashCodes吗 如果答案是否定的: 这意味着大多数时候bucket只包含一个节点,您能解释一下原因吗?因为根据许多不同的键可以有相同的bucket索引 实施如何确保密钥的良好分发?这可能意味着bucket表的大小与键的数量有关 即使表Buc

根据这个问题,

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

对我来说,它听起来并没有真正优化过,在使用
equals
之前,它不是比较了链表的
hashCode
s吗

如果答案是否定的:

这意味着大多数时候bucket只包含一个节点,您能解释一下原因吗?因为根据许多不同的键可以有相同的bucket索引

实施如何确保密钥的良好分发?这可能意味着bucket表的大小与键的数量有关

即使表Bucket大小等于键的数量,HashMap
hashCode
函数如何确保键的良好分布?这不是随机分布吗

能告诉我们更多细节吗

它不是比较链表的hachcode而不是使用 相等于

这不是必需的。hashcode用于确定bucket编号,无论是put还是get操作。一旦您使用hashcode知道了bucket编号,并在其中找到了一个链表,那么您就知道需要对其进行迭代,并需要检查是否相等才能找到确切的键。因此,这里不需要进行哈希代码比较

这就是为什么hashcode应该尽可能地唯一,以便最好地进行查找

这意味着在大多数情况下,bucket只包含1个节点


没有。这取决于hascode的唯一性。如果两个关键对象具有相同的hashcode但不相等,则bucket with包含两个节点

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

某些存储桶可能仍然包含多个项(称为“哈希冲突”),而其他存储桶将为空。但平均而言,大多数包含项目的存储桶只包含一个项目

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

良好的hashCode()实现可以维持密钥的良好分布;HashMap对此几乎没有影响。一个好的hashCode()方法是,返回的哈希值与对象的值具有尽可能随机的关系


例如,一个错误的哈希函数,从前,String.hashCode()方法只依赖于字符串的开头。问题是,有时您希望在一个HashMap中存储一组字符串,这些字符串的开头都是相同的(例如,指向单个网站上所有页面的URL),从而导致哈希冲突的比例过高。我相信String.hashCode()后来被修改来解决这个问题。

这个实现是开源的,所以我鼓励您只针对任何特定的问题。但总的想法是:

  • 好的hashCode分发的主要责任在于键的类,而不是HashMap。如果键的
    hashCode()
    方法的分发不好(例如,
    返回0;
    ),则HashMap将执行不好
  • HashMap确实做了一些“重新散列”,以确保稍微更好的分发,但并不多(请参阅)
  • 另一方面,对bucket中的每个元素都进行了一些检查(是的,它是作为链表实现的)
    • 首先,HashMap检查元素的hashCode和传入键的hashCode。这是因为这个操作很快,元素的hashCode在
      put
      时间被缓存。这可以防止元素具有不同的hashCode(根据hashCode的约定,它们是不相等的,由
      Object
      建立的相等),但恰好落在同一个bucket中(请记住,bucket索引基本上是
      hashCode%bucket.length
    • 如果成功,那么HashMap将显式检查
      equals
      ,以确保它们真正相等。请记住,相等意味着相同的hashCode,但相同的hashCode不需要相等(也不需要相等,因为某些类可能有无限多个不同的值,如
      String
      ,但可能的hashCode值只有有限的数量)

双重检查hashCode和equals的原因是既快速又正确。考虑两个具有不同哈希代码的键,但最终在同一个Hash映射桶中。例如,如果键A的hashCode=7,键B的hashCode=14,并且有7个bucket,那么它们都将在bucket 0中结束(
7%7==0
,和
14%7==0
)。检查hashcode有一种快速的方法可以看出a和B是不相等的。如果您发现hashCode是相等的,那么您可以通过调用
equals
来确保这不仅仅是hashCode冲突。这只是一个优化,真的;一般的hash map算法不需要它。

当我们将Key和Value object传递给Java HashMap上的put()方法时,HashMap实现调用Key object上的hashCode方法,并将返回的hashCode应用到自己的hashing函数中,以找到存储Entry object的bucket位置,需要提及的重要一点是,Java中的HashMap将键和值对象存储为
Map.Entry
,这对于理解检索逻辑至关重要

在检索某个键的值时,如果hashcode与其他一些键相同,则bucket位置将相同,并且HashMap中会发生冲突,因为HashMap使用LinkedList存储对象,该条目(Map的对象