在Java中,我有一个Java3D类Vertex
的子类Point3f
。现在Point3f
根据其坐标值计算equals()
,但对于我想要更严格的Vertex
类:如果它们是同一个对象,则两个顶点相等。到目前为止,这么好:
class Vertex extends Point3f {
// ...
public boolean equals(Object other) {
return this == other;
}
}
我知道这违反了equals()
的合同,但由于我只会将顶点与其他顶点进行比较,因此这不是问题。
现在,为了能够将顶点放入HashMap
,hashCode()
方法必须返回与equals()
一致的结果。它目前正在这样做,但可能将其返回值基于Point3f
的字段,因此将为具有相同坐标的不同Vertex
对象提供哈希冲突。
因此,我希望将hashCode()
基于对象的地址,而不是从Vertex
的字段计算它。我知道Object
类执行此操作,但我无法调用其hashCode()
方法,因为Point3f
会覆盖它。
所以,实际上我的问题是双重的:
equals()
?编辑:我只想到了一些东西......我可以在对象创建时生成一个随机int
值,并将其用于哈希码。这是一个好主意吗?为什么(不是)?
答案 0 :(得分:10)
使用System.identityHashCode()或使用IdentityHashMap。
答案 1 :(得分:1)
System.identityHashCode()
返回与默认方法hashCode()
返回的给定对象相同的哈希码,无论给定对象的类是否覆盖hashCode()
。
答案 2 :(得分:0)
即使此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();
}
}
答案 3 :(得分:0)
只是FYI,你的equals方法不违反equals契约(对于基础Object的契约)...这基本上是基础Object方法的equals方法,所以如果你想要identity而不是Vertex equals ,那很好。
至于哈希码,你真的不需要改变它,虽然接受的答案是一个很好的选择,如果你的哈希表包含许多具有相同值的顶点键,效率会更高。
您不需要更改它的原因是因为完全正确的哈希代码将为等于返回false的对象返回相同的值...它甚至是一个有效的哈希代码,只返回0所有每个实例的时间。这是否对哈希表有效是完全不同的问题...如果你的很多对象具有相同的哈希码,你会得到更多的冲突(如果你单独留下哈希码并且有很多顶点的情况可能就是这种情况)具有相同的值)。
请不要接受这个作为答案当然(你选择的更实用),我只是想给你一些关于哈希码和等于的更多背景信息; - )
答案 4 :(得分:0)
为什么要首先覆盖hashCode()?如果你想使用其他一些相等的定义,你会想要这样做。例如
公共类A { int id;
public boolean equals(A other){return other.id == id} public int hashCode(){return id;}
} 你想要清楚的是,如果id是相同的,那么对象是相同的,你覆盖hashcode,这样你就不能这样做:
HashSet hash = new HashSet(); hash.add(new A(1)); hash.add(new A(1)); 得到2相同(从你的平等定义的角度来看)A的。 那么正确的行为就是你在哈希中只有一个对象,第二个写将覆盖。
答案 5 :(得分:0)
由于您不使用equals作为逻辑比较,而是使用物理比较(即它是同一个对象),因此唯一可以保证哈希码将返回唯一值的方法是实现自己的变体建议。不要生成随机数,而是使用UUID为每个对象生成实际的唯一值。
System.identityHashCode()可以工作,大多数当时,但不能保证,因为Object.hashCode()方法不保证返回唯一每个对象的价值。我已经看到边缘情况发生了,它可能依赖于VM实现,这不是您希望代码依赖的东西。
摘自Object.hashCode()的javadoc: 尽可能合理,Object类定义的hashCode方法确实为不同的对象返回不同的整数。 (这通常通过将对象的内部地址转换为整数来实现,但JavaTM编程语言不需要此实现技术。)
这解决的问题是,当插入到散列映射中时,有两个单独的点对象互相覆盖,因为它们都具有相同的散列。由于没有逻辑等于,伴随着hashCode()的重写,identityHashCode方法实际上可以导致这种情况发生。如果逻辑情况只会替换同一逻辑点的哈希条目,那么使用基于系统的哈希可以使它与任何两个对象一起发生,相等(甚至类)不再是一个因素。
答案 6 :(得分:-2)
函数hashCode()继承自Object,完全按照您的意图工作(在对象级别,而不是坐标级别)。应该没有必要改变它。
至于你的equals-method,没有理由使用它,因为你可以在代码中执行obj1 == obj2而不是使用equals,因为它用于排序和类似,比较坐标会产生很多更有意义。