为什么 HashMap 的 get() 在 Java 中比较哈希值和键?



我正在研究JDK8中HashMap的实现。在 get 方法中,我看到了以下行,用于查找与给定键匹配的节点。

if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))

为什么需要将哈希值与键进行比较?为什么上面的行没有写成:

if (((k = e.key) == key) || (key != null && key.equals(k)))

有什么解释为什么这样做吗?谢谢。

似乎引起您困惑的是两件事:

1. 比较哈希值(通常(比直接比较键快得多。

2. 在 == 运算符中,如果第一个条件为 false,则不会检查第二个条件。

因此,首先比较哈希值,这很快:

  • 当它们不相等时,您知道键也不相等,您就完成了。

  • 当它们相等时,您不知道键是否也相等,因此您必须比较键,这(相对(慢。

由于大多数键不相等,因此大多数时候您只比较哈希。只有当键相等(或由于哈希冲突而导致哈希相等(时,您才会比较键,这种情况很少见,因此具有性能优势。

这是检查两个值是否可能相等的有效方法。

hashcode合同规定:

JavaDocs of Object.hashCode

如果根据 equals(Object( 方法将两个对象相等,则对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果。

因此,如果哈希是不同的,则执行进一步检查没有意义。

由于HashMap无论如何都需要键的哈希代码来选择要放入条目的存储桶,因此权衡是为每个条目存储额外的int,而不是可能必须重复计算它并且必须更频繁地执行键的equalsHashMap针对快速检索和插入进行了更多优化,而对内存效率则进行了较少优化。


旁注:HashMap依赖于密钥不会以任何方式改变它们在equalshashcode方面的"身份"——虽然这看起来很明显,但在 JavaDocs 中没有明确提及HashMap,并且过去曾引发过一些问题:可变哈希映射键是一种危险的做法吗? - 这包含在更一般的Map合同中:

注意:如果将可变对象用作映射键,则必须格外小心。如果对象的值更改方式影响等于比较,而对象是映射中的键,则不指定映射的行为。

最新更新