【问题标题】:How to compute the hashCode() from the object's address?如何从对象的地址计算 hashCode()?
【发布时间】:2010-09-12 20:02:17
【问题描述】:

在 Java 中,我有一个 Java3D 类 Point3f 的子类 Vertex。现在Point3f 根据其坐标值计算equals(),但是对于我的Vertex 类,我想要更严格:两个顶点只有在它们是同一个对象时才相等。到目前为止,一切顺利:

class Vertex extends Point3f {

    // ...

    public boolean equals(Object other) {
        return this == other;
    }
}

我知道这违反了equals() 的约定,但由于我只会将顶点与其他顶点进行比较,所以这不是问题。

现在,为了能够将顶点放入HashMaphashCode() 方法必须返回与equals() 一致的结果。它目前是这样做的,但它的返回值可能基于Point3f 的字段,因此会为具有相同坐标的不同Vertex 对象提供哈希冲突。

因此,我想将hashCode() 基于对象的地址,而不是从Vertex 的字段中计算它。我知道Object 类会这样做,但我不能调用它的hashCode() 方法,因为Point3f 会覆盖它。

所以,实际上我的问题是双重的:

  • 我还想要这么肤浅的equals()吗?
  • 如果是,那么如何获取对象的地址来计算哈希码?

编辑:我只是想到了一些事情......我可以在对象创建时生成一个随机的int 值,并将其用于哈希码。这是一个好主意吗?为什么(不)?

【问题讨论】:

    标签: java hash equals hashcode


    【解决方案1】:

    使用 System.identityHashCode() 或使用 IdentityHashMap。

    【讨论】:

    • 其实...这正是我所需要的 :D
    【解决方案2】:

    System.identityHashCode() 为给定对象返回与默认方法 hashCode() 返回相同的哈希码,无论给定对象的类是否覆盖 hashCode()

    【讨论】:

      【解决方案3】:

      即使answer 可能更好,您也可以使用委托。

      
      class Vertex extends Point3f{
         private final Object equalsDelegate = new Object();
         public boolean equals(Object vertex){
            if(vertex instanceof Vertex){
               return this.equalsDelegate.equals(((Vertex)vertex).equalsDelegate);
            }
            else{
               return super.equals(vertex);
            }
         }
         public int hashCode(){
            return this.equalsDelegate.hashCode();
         }
      }
      

      【讨论】:

        【解决方案4】:

        仅供参考,您的 equals 方法不违反 equals 合同(对于基本 Object 的合同)......这基本上是基本 Object 方法的 equals 方法,所以如果你想要身份 equals 而不是 Vertex equals ,没关系。

        至于哈希码,您确实不需要更改它,尽管接受的答案是一个不错的选择,并且如果您的哈希表包含许多具有相同值的顶点键,效率会更高。

        您不需要更改它的原因是,对于等于返回 false 的对象,哈希码将返回相同的值是完全没问题的......它甚至是一个有效的哈希码,只返回 0 所有每个实例的时间。这对哈希表是否有效是完全不同的问题......如果您的许多对象具有相同的哈希码(如果您单独留下哈希码并且有很多顶点,可能会出现这种情况)具有相同的值)。

        当然,请不要接受这个作为答案(你选择的更实用),我只是想给你一些关于哈希码和等于的背景信息;-)

        【讨论】:

        • 它确实违反了合同,因为 Point3f.equals(Vertex) 可以返回 true,而相反的调用总是返回 false。因此,我的实现不是对称的。
        • 我也意识到默认的 hashCode() 是有效的,但它做的不仅仅是必要的。而且由于我经常使用 HashTable 和 HashSet,我认为这将是性能上的胜利。
        • 嗯,它违反了 Point3f 的 equals 方法,而不是 Object 的 equals 方法......这就是我所指的(以及哈希表对什么感兴趣)
        【解决方案5】:

        为什么首先要覆盖 hashCode()?如果您想使用其他一些平等定义,您会想要这样做。例如

        公开课 A { int id;

        public boolean equals(A other) { return other.id==id} public int hashCode() {return id;}

        } 您想清楚,如果 id 相同,则对象相同,并且您覆盖哈希码,因此您不能这样做:

        HashSet hash= new HashSet(); hash.add(新 A(1)); hash.add(新 A(1)); 并获得2个相同的(从您对平等的定义的角度来看)A。 正确的行为是哈希中只有 1 个对象,第二次写入会覆盖。

        【讨论】:

          【解决方案6】:

          由于您不是将等号用作逻辑比较,而是物理比较(即它是同一个对象),因此保证哈希码返回唯一值的唯一方法是实现您自己的变体建议。不用生成随机数,而是使用 UUID 为每个对象生成一个实际的唯一值。

          大多数情况下 System.identityHashCode() 会起作用,但不能保证,因为 Object.hashCode() 方法保证返回唯一的每个对象的价值。我已经看到发生了边缘情况,它可能会依赖于 VM 实现,这不是你希望你的代码依赖的东西。

          Object.hashCode() 的 javadocs 节选: 在合理可行的情况下,由 Object 类定义的 hashCode 方法确实为不同的对象返回不同的整数。 (这通常通过将对象的内部地址转换为整数来实现,但 JavaTM 编程语言不需要这种实现技术。)

          这里解决的问题是两个独立的点对象在插入哈希映射时不会相互覆盖,因为它们都具有相同的哈希。由于没有逻辑上的等号,伴随着对 hashCode() 的覆盖,identityHashCode 方法实际上会导致这种情况发生。在逻辑案例只会替换相同逻辑点的哈希条目的情况下,使用基于系统的哈希可能会导致它与任何两个对象一起发生,相等(甚至类)不再是一个因素。

          【讨论】:

          • System.identityHashCode() 很好,因为 hashCode() 不期望返回不同对象的不同值;只需要为相等的对象返回相等的值。
          【解决方案7】:

          函数 hashCode() 继承自 Object 并且完全按照您的意图工作(在对象级别,而不是坐标级别)。应该没必要改吧。

          至于您的 equals 方法,甚至没有理由使用它,因为您可以只在代码中执行 obj1 == obj2 而不是使用 equals,因为它是用于排序和类似的,比较坐标很重要更有意义。

          【讨论】:

            猜你喜欢
            • 2013-12-22
            • 2020-01-26
            • 1970-01-01
            • 1970-01-01
            • 2023-03-08
            • 1970-01-01
            • 2012-03-16
            • 2020-10-16
            • 2012-08-01
            相关资源
            最近更新 更多