可能重复:
In Java, why must equals() and hashCode() be consistent?
我读到在覆盖equals()时应始终使用code()。
任何人都可以提供一个实际的例子,说明为什么它可能会出错? 即重写equals()但不能覆盖hashCode()时可能出现的问题。
每当我们覆盖equals()时,是否有必要编写一个健壮的hasCode()函数?或者一个简单的实现就足够了?
例如,
如下所示的不良实现足以满足equals()和&之间的契约。 hashCode()方法
public int hashCode() {
return 91;
}
答案 0 :(得分:4)
equals
和hashcode
都基于对象的独特性原则。如果equals
返回true
,则两个对象的哈希码必须相同,否则基于哈希的结构和算法可能会有不确定的结果。
考虑基于散列的结构,例如HashMap
。 hashcode
将被调用作为获取密钥引用的基础,而不是equals
,这使得在大多数情况下无法找到密钥。此外,hashcode
的不良实现会产生冲突(多个具有相同hashcode
的对象,哪一个是“正确的”?)会影响性能。
恕我直言,覆盖equals
或hashcode
(而不是覆盖两者)应被视为代码气味,或者至少是潜在的错误来源。也就是说,除非你100%确定它迟早不会影响你的代码(我们什么时候才能确定?)。
注意:有各种库通过equals
和hashcode
构建器提供支持,例如Apache Commons HashcodeBuilder
和EqualsBuilder
。
答案 1 :(得分:2)
equals()
和hashCode()
在某些集合中结合使用,例如HashSet
和HashMap
,因此您必须确保如果使用这些集合,则覆盖hashCode
根据合同。
如果您根本没有覆盖hashCode
,那么HashSet
和HashMap
就会出现问题。特别是,两个“相等”的对象可以放在不同的散列桶中,即使它们应该相等。
如果你覆盖hashCode
,但做得不好,那么你就会遇到性能问题。 HashSet
和HashMap
的所有条目都将放入同一个存储桶中,您将失去O(1)性能并改为使用O(n)。这是因为数据结构基本上变成了线性检查的链表。
至于在这些条件之外打破程序,它不太可能,但你永远不知道API(特别是在第三方库)何时将依赖于此合同。对于不实现其中任何一个的对象,都支持该契约,因此可以想象一个库可以在不使用哈希桶的情况下依赖于此。
在任何情况下,实现好hashCode
都很容易,尤其是在使用IDE的情况下。 Eclipse和Netbeans都能够以遵循所有合同的方式为您生成equals
和hashCode
,包括equals
的反向规则(a.equals(b) == b.equals(a)
的断言)。您只需选择要包含的字段即可。
答案 2 :(得分:2)
这里有一些代码说明了你可以通过不实现hashCode()来引入的错误:Set.contains()将首先检查对象的hashCode(),然后检查.equals()。因此,如果您不同时实现这两者,.contains()将不会以直观的方式运行:
public class ContainsProblem {
// define a class that implements equals, without implementing hashcode
class Car {
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Car)) return false;
Car car = (Car) o;
if (name != null ? !name.equals(car.name) : car.name != null) return false;
return true;
}
public String getName() {return name;}
public Car(String name) { this.name = name;}
}
public static void main(String[] args) {
ContainsProblem oc = new ContainsProblem();
ContainsProblem.Car ford = oc.new Car("ford");
ContainsProblem.Car chevy = oc.new Car("chevy");
ContainsProblem.Car anotherFord = oc.new Car("ford");
Set cars = Sets.newHashSet(ford,chevy);
// if the set of cars contains a ford, a ford is equal to another ford, shouldn't
// the set return the same thing for both fords? without hashCode(), it won't:
if (cars.contains(ford) && ford.equals(anotherFord) && !cars.contains(anotherFord)) {
System.out.println("oh noes, why don't we have a ford? isn't this a bug?");
}
}
}
答案 3 :(得分:1)
您的琐碎实现是正确的,但会破坏基于散列的集合的性能。
默认实现(由Object
提供)会破坏合同,如果您的类的两个不同实例相等。
答案 4 :(得分:0)
我建议阅读Joshua Bloch的“Effective Java”第3章“所有对象共有的方法”。没有人能比他更好地解释。他领导了许多Java平台功能的设计和实现。