如何从对象的地址计算hashCode()?

时间:2020-03-06 14:40:47  来源:igfitidea点击:

在Java中,我有Java3D类Point3f的子类Vertex。现在,Point3f根据其坐标值来计算equals(),但是对于我的Vertex类,我想变得更严格:两个顶点仅在它们是同一个对象的情况下才相等。到目前为止,一切都很好:

class Vertex extends Point3f {

    // ...

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

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

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

因此,我想以对象的地址为基础的hashCode(),而不是从Vertex的字段中进行计算。我知道Object类可以做到这一点,但是我不能调用它的hashCode()方法,因为Point3f会覆盖它。

因此,实际上我的问题是双重的:

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

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

解决方案

函数hashCode()继承自Object,并且完全按预期工作(在对象级别,而不是坐标级别)。无需更改它。

至于equals-method,甚至没有理由使用它,因为我们可以在代码中执行obj1 == obj2而不是使用equals,因为它是用于排序和类似操作的,比较坐标更有意义。

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

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

我们可以使用委托,即使此答案可能更好。

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();
   }
}

仅供参考,equals方法不会违反equals合同(对于基础对象的合同而言)...这基本上是基础Object方法的equals方法,因此,如果我们要使用身份等于而不是Vertex等于,美好的。

至于哈希码,虽然接受的答案是一个不错的选择,但是如果哈希表包含许多具有相同值的顶点键,那么答案确实是一个不错的选择,并且效率会大大提高。

不需要更改它的原因是因为哈希码将为对象返回相同的值(等于返回false)是完全可以的...它甚至是始终每次都返回0的有效哈希码实例。这对于哈希表是否有效完全不同的问题...如果许多对象具有相同的哈希码,我们将遇到更多的冲突(如果我们单独保留哈希码并且有很多顶点,则可能会发生这种情况。具有相同的值)。

当然,请不要接受这个作为答案(我们选择的内容更加实用),我只是想向我们提供更多有关哈希码和等号的背景信息;

为什么首先要覆盖hashCode()?如果要使用其他相等性定义,则需要这样做。例如

公开课A {
int id;

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

}
我们需要明确的一点是,如果ID相同,则对象相同,并且我们覆盖了哈希码,因此无法执行以下操作:

HashSet hash = new HashSet();
hash.add(new A(1));
hash.add(new A(1));
并得到2个相同的(从相等性定义的角度来看)A。
正确的行为是我们在哈希中只有1个对象,第二次写入将被覆盖。

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

System.identityHashCode()在大多数情况下都可以使用,但由于不能保证Object.hashCode()方法返回每个对象的唯一值,因此无法保证。我已经看到了边际情况的发生,它可能取决于VM的实现,这不是我们希望代码依赖的事情。

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

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