【问题标题】:IdentityHashCode in HashMap's bucketHashMap 的桶中的 IdentityHashCode
【发布时间】:2019-05-08 13:33:00
【问题描述】:

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 它将如何打破关系以及如何构造树?

我的意思是 - bucket 将变成一棵树,并使用System.identityHashCode 打破平局。 然后我将尝试使用不同的实例调用containsKey 方法(这将具有相同的hashCodea.equals(b) == true)它将具有不同的identityHashCode 所以树可能会被错误的节点遍历(左而是正确的)并且它不会找到密钥?

是我遗漏了什么还是这是正常行为?

【问题讨论】:

  • 如果您有两个对象ab,其中a.equals(b) 为真,那么调用get 返回哪个对象又有什么关系?
  • 我猜红黑树不会使用equals,而是比较元素。
  • @rgettman get 两者都不会返回,因为它返回一个 valueequals 是在 keys 上调用的。

标签: java java-8 hashmap


【解决方案1】:

在插入期间存储桶将使用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()(负责比较identityHashCodes 的方法不会在find() 中的任何地方调用。

【讨论】:

  • 它怎么知道他需要扫描两个子树?这对我来说毫无意义。你能解释一下吗?
  • 好吧,它知道(因为它检查了一系列ifs)没有办法将当前树节点与它正在寻找的键进行比较,但它们并不相等.所以我们正在寻找的东西可能在任何地方。正如你所指出的,我们不能在这里依赖identityHashCode,所以我们必须搜索树的两个部分。
  • 没错。我添加了一个答案,解释了为什么它确实使用身份哈希码进行插入,尽管它不使用它进行查找。
【解决方案2】:

在引用部分之前解释了身份哈希码基础平局打破的动机:

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 进行探测。

【讨论】:

    【解决方案3】:

    不,您确实基于System::identityHashCode 将条目向左或向右移动,但在那个桶中,仍然 具有相同 hashCode 的条目(当然不一样,只有重要的部分)。

    因此,当您搜索某些内容时,有时必须同时查看leftright,没有办法绕过它,就这么简单。

    【讨论】:

      猜你喜欢
      • 2016-02-06
      • 2013-09-09
      • 2018-06-10
      • 2014-12-06
      • 2020-07-03
      • 1970-01-01
      • 1970-01-01
      • 2021-06-02
      • 1970-01-01
      相关资源
      最近更新 更多