HashMap存储桶中的IdentityHashCode

时间:2018-12-06 19:33:49

标签: java java-8 hashmap

HashMap的实现细节中,我可以阅读:

When using comparators on insertion, to keep a
 * total ordering (or as close as is required here) across
 * rebalancings, we compare classes and identityHashCodes as
 * tie-breakers.

如果我有一个常数hashCode和一个很好的equals,而我的班级没有实现Comparable,它将如何打破束缚以及如何构造树?

我的意思是-桶将转换为树,并使用System.identityHashCode打破平局。 然后,我将尝试使用不同的实例(具有相同的containsKeyhashCode)调用a.equals(b) == true方法,它将具有不同的identityHashCode,因此树是否有可能被错误的节点遍历(左转右),并且找不到键?

我错过了什么吗?或者这是正常现象?

3 个答案:

答案 0 :(得分:8)

在引用的部分之前直接说明了破坏身份哈希码基础并列的动机:

HashMap.java, line 212

* When bin lists are treeified, split, or untreeified, we keep 
* them in the same relative access/traversal order (i.e., field 
* Node.next) to better preserve locality, and to slightly 
* simplify handling of splits and traversals that invoke 
* iterator.remove. When using comparators on insertion, to keep a 
* total ordering (or as close as is required here) across 
* rebalancings, we compare classes and identityHashCodes as 
* tie-breakers. 

因此,通过身份哈希码进行排序可以提供稳定的排序,以帮助实现拆分和Iterator.remove()操作(必须支持一致地继续遍历)。

this answer中所述,它不用于查找操作,就像您在问题中已经说过的那样,两个相等的对象可能具有不同的标识码。因此,对于具有相同哈希码但未实现Comparable的不相等对象,无法遍历所有对象并通过equals进行探测。

答案 1 :(得分:6)

存储桶在插入期间将使用identityHashCode,但是查找仅使用哈希码和compare()调用(如果有)。这意味着有时需要扫描节点的两个子树。

查找逻辑在此行

do {
  if (... keys are equal or can be compared ...) {
    // Go left, right or return the current node
    ...
  } else if ((q = pr.find(h, k, kc)) != null)
    // Search the right subtree recursively
    return q;
  else
   // Go to the left subtree
   p = pl;
} while (p != null);

请参见http://hg.openjdk.java.net/jdk10/jdk10/jdk/file/ffa11326afd5/src/java.base/share/classes/java/util/HashMap.java#l1901,并注意tieBreakOrder()(负责比较identityHashCode的方法在find()中的任何地方都没有调用。

答案 2 :(得分:1)

不,您实际上是根据System::identityHashCode向左或向右移动条目,但是在该存储桶中,有 still 个条目具有相同的hashCode(不是一样,只是一部分很重要)。

因此,当您搜索某项内容时,有时必须同时查看leftright,却没有办法如此简单。