覆盖equals()时覆盖hashCode()

时间:2012-12-10 16:03:09

标签: java

  

可能重复:
  In Java, why must equals() and hashCode() be consistent?

我读到在覆盖equals()时应始终使用code()。

任何人都可以提供一个实际的例子,说明为什么它可能会出错? 即重写equals()但不能覆盖hashCode()时可能出现的问题。

每当我们覆盖equals()时,是否有必要编写一个健壮的hasCode()函数?或者一个简单的实现就足够了?

例如,

如下所示的不良实现足以满足equals()和&之间的契约。 hashCode()方法

public int hashCode() {
   return 91;
}

5 个答案:

答案 0 :(得分:4)

equalshashcode都基于对象的独特性原则。如果equals返回true,则两个对象的哈希码必须相同,否则基于哈希的结构和算法可能会有不确定的结果。

考虑基于散列的结构,例如HashMaphashcode将被调用作为获取密钥引用的基础,而不是equals,这使得在大多数情况下无法找到密钥。此外,hashcode的不良实现会产生冲突(多个具有相同hashcode的对象,哪一个是“正确的”?)会影响性能。

恕我直言,覆盖equalshashcode(而不是覆盖两者)应被视为代码气味,或者至少是潜在的错误来源。也就是说,除非你100%确定它迟早不会影响你的代码(我们什么时候才能确定?)。

注意:有各种库通过equalshashcode构建器提供支持,例如Apache Commons HashcodeBuilderEqualsBuilder

答案 1 :(得分:2)

equals()hashCode()在某些集合中结合使用,例如HashSetHashMap,因此您必须确保如果使用这些集合,则覆盖hashCode根据合同。

如果您根本没有覆盖hashCode,那么HashSetHashMap就会出现问题。特别是,两个“相等”的对象可以放在不同的散列桶中,即使它们应该相等。

如果你覆盖hashCode,但做得不好,那么你就会遇到性能问题。 HashSetHashMap的所有条目都将放入同一个存储桶中,您将失去O(1)性能并改为使用O(n)。这是因为数据结构基本上变成了线性检查的链表。

至于在这些条件之外打破程序,它不太可能,但你永远不知道API(特别是在第三方库)何时将依赖于此合同。对于实现其中任何一个的对象,都支持该契约,因此可以想象一个库可以在不使用哈希桶的情况下依赖于此。

在任何情况下,实现好hashCode都很容易,尤其是在使用IDE的情况下。 Eclipse和Netbeans都能够以遵循所有合同的方式为您生成equalshashCode,包括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平台功能的设计和实现。