我的hashCode
方法确保相等的对象具有相等的哈希码。我的实现还保证了对负性为真,因为相等哈希码意味着对象相等。执行如下命令:
@Override
public boolean equals(Object obj) {
if (this == obj) {return true;}
if (obj == null) {return false;}
if (getClass() != obj.getClass()) {return false;}
return (obj.hashCode() == this.hashCode());
}
似乎是一个显而易见的选择,但我不经常看到这个。这对我来说是个好主意,还是我遗漏了什么?
另外,我不确定这是否相关,但是hashCode
和equals
方法是在生成的类中(显然是自己生成的)。我提到这一点的原因是要强调这样一个事实,即这些方法不会每次都手工维护。它们位于编译时创建的类中,除非它们所基于的架构发生了变化,否则不打算对其进行更改。
如果,正如问题所说,您的hashCode
实现确实保证不相等的对象具有不相等的哈希码,那么原则上您所做的事情没有任何问题。你的equals
方法正在检查一个等价于相等的属性,所以行为是预期的。不过,你应该重新检查这个保证;请记住,无论你多么聪明,你只能在int类型中存储32位的信息,这意味着大多数非平凡对象必然会有哈希冲突。
然而,有一些注意事项。首先是清晰:如果你以这种方式实现equals
,任何使用你代码的人都必须看看你的hashCode
实现,以找出哪些属性决定了平等。其次是速度:如果您的哈希码实现比简单地检查相关属性的相等性(包括该计算中的布尔捷径)要慢得多,那么可能值得在equals
函数中重新实现逻辑以避免速度损失。第三是耦合:以这种方式实现equals
意味着它依赖于hashCode
的属性,并且将来对hashCode
的任何更改都必须保留该属性或将逻辑移动到equals
。
这不是一个好主意。两个相等的对象必须具有相同的哈希码,反之则不成立:具有相同哈希码的两个对象可能不相等。作为一个简单的例子,考虑Long
。哈希码返回int
,因此不可能用int表示所有可能的long
值。这意味着许多Long
对象不相等,但具有相同的哈希码。
当且仅当你的对负保证成立,那么你可以用这种方式测试等式。一般情况下,你的对负命题不成立,也不能保证相等。特别是有人可能会重写hashCode()
方法,因此您的对负性不再有效。最好使用更传统的相等性检验,比较显著字段。
你的代码是脆弱的,并且严重依赖于你的hashCode()
实现的一个非常特定的属性,而这个属性不是一般hashCode()
合约的一部分。您总是可以使用不匹配的哈希码来检测不相等。使用相等的哈希码来假定相等并不是好的做法。
有时候(虽然很少),散列码会导致冲突;你可能已经意识到了这一点!
因此,在被比较对象的其他特征(可能除了哈希码)上建立相等性是一个好主意。
不,散列用于在集合中分发对象,它们不是唯一的。例如,集合将使用哈希码来标识放置对象的存储桶。桶本身是一个列表,包含所有具有相似哈希码的对象。目的是为了在顺序不重要的情况下提高查找效率。
使用相同的相等属性构建散列是很重要的,以确保两个语义上相等的对象最终在相同的bucket