Java 当equals()基于多个独立字段时,hashCode()方法
我有一个类,它的相等性基于两个字段,如果其中一个字段相等,那么这种类型的对象就被认为相等。如何为这样的equals()编写hashCode()函数,以便在equals返回true时保持hashCode相等的一般约定Java 当equals()基于多个独立字段时,hashCode()方法,java,equals,hashcode,Java,Equals,Hashcode,我有一个类,它的相等性基于两个字段,如果其中一个字段相等,那么这种类型的对象就被认为相等。如何为这样的equals()编写hashCode()函数,以便在equals返回true时保持hashCode相等的一般约定 public class MyClass { int id; String name; public boolean equals(Object o) { if (!(o instanceof MyClass)) return false;
public class MyClass {
int id;
String name;
public boolean equals(Object o) {
if (!(o instanceof MyClass))
return false;
MyClass other = (MyClass) o;
if (other.id == this.id || other.name == this.name)
return true;
return false;
}
}
如何为这个类编写hashCode()函数?我想避免在这里返回一个常数这样的小例子:
public int hashCode() {
return 1;
}
最简单的方法是对每个字段的哈希码进行异或运算。在某些情况下(例如,在X,Y坐标中,当翻转X和Y时,可能会导致哈希相等的情况),但总体上相当有效。根据需要调整以减少碰撞(如有必要),以提高效率。如何
public override int GetHashCode()
{
return id.GetHashCode() ^ name.GetHashCode();
}
我认为不存在非平凡的哈希代码。此外,您的
equals()
违反了一般合同,因为它是不可传递的:
(1,2)
等于(1,3)
(4,3)
等于(1,3)
但是(4,3)
不等于(1,2)
为了完整起见,我向您提供-proof=) 声明:哈希代码必须是平凡常量函数。 证明:让
(a,b)
和(c,d)
是两个具有不同哈希码的对象,即h(a,b)≠ h(c,d)
。考虑对象<代码>(a,d)< /代码>。根据OP的定义,(a,d)
等于(a,b)
,(a,d)
等于(c,d)
。根据公式可知,h(a,d)=h(a,b)=h(c,d)
;矛盾。这个怎么样
public override int GetHashCode()
{
return (id.ToString() + name.ToString()).GetHashCode();
}
函数应该始终返回一个“有效”哈希
编辑:刚刚注意到您使用了“或“不”和:p我怀疑这个问题是否有任何好的解决方案…我非常肯定Zach是对的-没有非平凡的哈希代码来做这件事 伪证明: 考虑任意两个不相等的值,X=(id1,name1)和Y=(id2,name2)
现在考虑Z=(ID2,NAME1)。这等于X和Y,因此必须具有与X和Y相同的哈希代码。因此X和Y必须具有相同的哈希代码-这意味着所有值必须具有相同的哈希代码
你陷入一种奇怪的境地是有原因的——你打破了平等的传递性。事实上,X.equals(Z)和Z.equals(Y)应该意味着X.equals(Y)——但事实并非如此。你对平等的定义不适用于一般的平等契约。我想你不能。原因是,您的
equals()
方法是不可传递的
传递性是指三个非空的x,y,z,如果x.equals(y)
,y.equals(z)
,则x.equals(z)
。在您的示例中,对象x={id:1,名称:“ha”}
,y={id:1,名称:“foo”}
,z={id:2,名称:“bar”}
具有此属性(x.equals(y)和y.equals(z))
。但是,x.equals(z)
是false
。每个equals()
方法都应该有这个属性,请参阅Java API文档
回到散列函数:每个函数产生一个由
f(x)==f(y)
定义的等价性。这意味着,如果您对函数值进行比较,并希望它返回true,如果<代码> x==y(也可能在其他情况下),则您将接收传递关系,这意味着至少必须考虑对象等价性的传递闭包。在您的例子中,传递闭包是平凡的关系(一切等于一切)。这意味着您不能通过任何函数来区分不同的对象。好的,在您的场景中,忽略API要求一秒钟,没有非常量散列函数
假设有一个hashfunction,它的
(a,b),(a,c),b=c、 然后散列(a,b)!=hash(a,c),eventhough(a,b)=(a,c)
类似地,(b,a)和(c,a)必须发出相同的哈希代码
让我们调用散列函数h。我们发现:
对于所有x,y,v,w,h(x,y)=h(x,w)=h(v,w)
因此,唯一的hashFunction可以实现您想要的功能是常量。编辑:我没有仔细阅读这个问题 -- 我将使用commons lang jar XOR成员哈希代码应该可以工作。因为它们应该正确地实现hashCode()和equals() 但是,如果不保护哈希代码,代码可能会出错。 一旦它被散列,就不应该更改它。应该防止这种情况发生
public hashCode(){
return new AssertionError();
}
或
或
在重读问题之后 当其中一个字段被更新时,您可以自动完成另一个字段 -- 编辑:我的代码可能比我的英语更好
void setName(String value){
this.id=Lookup.IDbyName(value);
}
void setID(String value){
this.name=Lookup.NamebyId(value);
}
编辑2:
问题上的代码可能会出错,因为除非您同时设置了id和名称,否则将始终返回true
如果您确实想要一个执行部分相等的方法,请创建自己的名为“partialEquals()”的API。您是否有意将相等定义为ID相等或名称相等。。“或”应该是“和”吗 如果您的意思是“AND”,那么您的hashcode应该使用与equals()相同或更少的字段(但绝不使用equals未使用的字段)进行计算
如果您的意思是“或”,则hashgcode不应在其hashcode计算中包含id或名称,这没有真正意义。这并不能正确回答问题-当只有一个字段相同时,hashcode需要相同。您的答案要求两个字段提供相同的相等哈希值。这不适合这个问题,我也误读了这个问题。如果两个字段中的任何一个发生变化,hashcode就会发生变化。在你们的解决方案中,难道你们没有将他的equal从(other.id==this.id | | other.name==this.name)改为(other.id==this.id&&other.name==this.name)?否-这将为一个字段中不同的值生成不同的hash,而不是
public class MyClass {
private int id;
private String name;
boolean hashed=false;
public void setId(int value){
if(hashed)throw new IllegalStateException();
this.id=value;
}
public void setName(String value){
if(hashed)throw new IllegalStateException();
this.name=value;
}
// your equals() here
public hashCode(){
hashed=true;
return new HashCodeBuilder().append(id).append(name).toHashCode();
}
}
void setName(String value){
this.id=Lookup.IDbyName(value);
}
void setID(String value){
this.name=Lookup.NamebyId(value);
}