在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
打破平局。
然后,我将尝试使用不同的实例(具有相同的containsKey
和hashCode
)调用a.equals(b) == true
方法,它将具有不同的identityHashCode
,因此树是否有可能被错误的节点遍历(左转右),并且找不到键?
我错过了什么吗?或者这是正常现象?
答案 0 :(得分:8)
在引用的部分之前直接说明了破坏身份哈希码基础并列的动机:
* 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(不是一样,只是一部分很重要)。
因此,当您搜索某项内容时,有时必须同时查看left
和right
,却没有办法如此简单。