为什么实现Object.equals()方法时不使用hashCode()方法?

8

或者 "为什么Sun/Oracle公司总是要求我们每次都覆盖equals()和hashCode()方法?"

众所周知,如果您重写一个对象的equals()方法或hashCode()方法,必须同时重写另一个方法,因为这两个方法之间存在一种约定:

请注意,通常需要在重写此方法 [即equals()] 时重写hashCode()方法,以便维护hashCode()方法的一般约定,该约定规定相等的对象必须具有相等的哈希码。 -- Object.equals()的API文档

为什么Object类没有实现这种方式呢:

public boolean equals(Object obj) {
    return this.hashCode() == obj.hashCode()
}

如果他们这样做了,就可以避免全世界都要实现这两种方法。只覆盖hashCode()方法就足够了。
我猜这些人有不这样做的好理由,但我看不出来——请帮我澄清一下。

3
可能是因为这个原因:如果两个对象相等,则它们的哈希码也相等,但是如果两个对象的哈希码相等,则不能保证这些对象本身是相等的,因为散列冲突是可能的。 - Jonathan Apodaca
2
дёҚеҗҢзҡ„еҜ№иұЎд»Қ然еҸҜд»Ҙе…·жңүзӣёеҗҢзҡ„hashcode()гҖӮеҸӘжңү2^32дёӘж•ҙж•°гҖӮ - Daniel Fischer
3
常见的逻辑错误:x -> y并不意味着y -> x - asteri
1
顺便提一下,你的代码可以更简短,写成this.hashCode() == obj.hashCode()。这样也可以避免自动装箱(但仍然是不正确的)。 - Joachim Sauer
3个回答

12
如果a.equals(b)返回true,那么a.hashCode() == b.hashCode()必须评估为true
相反的情况是不正确的!完全可以有两个对象,其中a.hashCode() == b.hashCode()为true,但a.equals(b)为false。
事实上,这是必要的。对于hashCode(),有232个可能的返回值。在任何给定时刻,JVM都可以容纳超过232个对象(假设有足够的内存,这在今天是很可能的)。假设没有任何对象彼此相等(很容易做到,只需让它们成为"s1""s2"等),那么你就会绑定到校验和的冲突(见鸽巢原理)。
事实上,这是每个类的最简单的hashCode实现,它是正确的(但否则是非常糟糕的)。
public int hashCode() {
  return 0;
}

它神奇地满足了通用hashCode()协议的所有要求。

* 除了那些已经定义和记录了必须实现hashCode算法的类,最典型的例子就是String.hashCode()


不错的观点,但是hashCode()的文档也告诉我们:“然而,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。” 但是这个建议与标准实现不一致。如果您的类可以拥有超过2^32个不同的实例,则仍然可以重写equals()方法。 - Francois Bourgeois
字符串的一个例子是“FB”和“Ea”。 - arshajii
1
@FrancoisBourgeois:是的,这就是为什么我上面展示的实现方式是一个糟糕的想法。即使在那些可能的情况下,生成完美哈希也并不总是容易的。因此,假设它总是被实现,是一个相当冒险的举动。 - Joachim Sauer

3

Joachim是正确的,但还有另一个原因:效率。

计算哈希码可能很昂贵,如果调用equals(),但从未调用hashCode(),则这种努力将是不必要的。

有许多情况会出现这种情况;只有像Hashtable(或使用它的类)这样的类才会调用hashCode()


哎呀!通常情况下,equals() 比相应的 hashCode() 调用更快(特别是当对象不相等时)。 - Joachim Sauer

1

有无限数量的对象具有相同的哈希码。这意味着您不能仅比较哈希码。

一个简单的例子是Long.hashCode():每个值为1L << 32 + 1的倍数的Long都具有哈希码0


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接