为什么我需要覆盖 Java 中的 equals 和 hashCode 方法?

问题描述:

最近我通读了这个 开发者作品文档.

该文档是关于有效且正确地定义 hashCode()equals(),但是我无法弄清楚为什么我们需要覆盖这两个方法.

The document is all about defining hashCode() and equals() effectively and correctly, however I am not able to figure out why we need to override these two methods.

我如何决定有效地实施这些方法?

How can I take the decision to implement these methods efficiently?

Joshua Bloch 谈 Effective Java

Joshua Bloch says on Effective Java

您必须在每个覆盖 equals() 的类中覆盖 hashCode().不这样做将导致违反 Object.hashCode() 的一般契约,这将阻止您的类与所有基于哈希的集合(包括 HashMap、HashSet 和 Hashtable)一起正常运行.

You must override hashCode() in every class that overrides equals(). Failure to do so will result in a violation of the general contract for Object.hashCode(), which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable.

让我们通过一个例子来理解它,如果我们覆盖 equals() 而不覆盖 hashCode() 并尝试使用 Map代码>.

Let's try to understand it with an example of what would happen if we override equals() without overriding hashCode() and attempt to use a Map.

假设我们有一个这样的类,并且 MyClass 的两个对象相等,如果它们的 importantField 相等(使用 hashCode()equals() 由eclipse生成)

Say we have a class like this and that two objects of MyClass are equal if their importantField is equal (with hashCode() and equals() generated by eclipse)

public class MyClass {
    private final String importantField;
    private final String anotherField;

    public MyClass(final String equalField, final String anotherField) {
        this.importantField = equalField;
        this.anotherField = anotherField;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((importantField == null) ? 0 : importantField.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final MyClass other = (MyClass) obj;
        if (importantField == null) {
            if (other.importantField != null)
                return false;
        } else if (!importantField.equals(other.importantField))
            return false;
        return true;
    }
}


想象一下你有这个


Imagine you have this

MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");

仅覆盖equals

如果只有 equals 被覆盖,那么当你调用 myMap.put(first,someValue) 时,首先会散列到某个桶,当你调用 myMap.put(second,someOtherValue) 它将散列到另一个存储桶(因为它们有不同的 hashCode).所以,虽然它们是相等的,但由于它们没有散列到同一个桶中,地图无法意识到它,它们都留在地图中.

If only equals is overriden, then when you call myMap.put(first,someValue) first will hash to some bucket and when you call myMap.put(second,someOtherValue) it will hash to some other bucket (as they have a different hashCode). So, although they are equal, as they don't hash to the same bucket, the map can't realize it and both of them stay in the map.

虽然如果我们重写 hashCode() 就没有必要重写 equals(),但让我们看看在这种特殊情况下会发生什么,我们知道两个对象MyClass 如果它们的 importantField 相等,则它们相等,但我们不会覆盖 equals().

Although it is not necessary to override equals() if we override hashCode(), let's see what would happen in this particular case where we know that two objects of MyClass are equal if their importantField is equal but we do not override equals().

仅覆盖hashCode

如果你只覆盖 hashCode 那么当你调用 myMap.put(first,someValue) 时,它首先计算它的 hashCode 并存储它在给定的存储桶中.然后当你调用 myMap.put(second,someOtherValue) 时,它应该按照 地图文档 因为它们是相等的(根据业务需求).

If you only override hashCode then when you call myMap.put(first,someValue) it takes first, calculates its hashCode and stores it in a given bucket. Then when you call myMap.put(second,someOtherValue) it should replace first with second as per the Map Documentation because they are equal (according to the business requirement).

但问题是 equals 没有重新定义,所以当映射散列 second 并遍历存储桶时,查找是否有对象 k 使得 second.equals(k) 为真它不会找到任何作为 second.equals(first) 将是 false.

But the problem is that equals was not redefined, so when the map hashes second and iterates through the bucket looking if there is an object k such that second.equals(k) is true it won't find any as second.equals(first) will be false.

希望很清楚