Why do you have to override hashCode to override equals

Keywords: Java

equals common interview questions

Before we start talking, let's look at some common interview questions to see if you can answer them all.

  • 1. What's the difference between equals and = =?
  • 2. Must two objects with equal hashcode be equal? Are equals equal?
  • 3. Two objects are equal by equals. Are their hashcode s equal?

If we do not override equals and hashcode, it uses the implementation of the Object method. Let's take a brief look first

public boolean equals(Object obj) {
    return (this == obj);
}
public static int hashCode(Object o) {
    return o != null ? o.hashCode() : 0;
}

Why override equals

As can be seen from the above code, when comparing the equals provided by Object, it is not the value comparison, but the memory address comparison. It can be seen that to compare objects with equals, equals must be overridden.

What is the problem with rewriting equals without rewriting hashCode

Let's look at the following passage first

hashCode must be overridden in every class that overrides the equals method. If you do not do so, you will violate the general convention of hashCode, that is, what is said in the above note. As a result, this class cannot be combined, so it works normally with the hash set, which refers to HashMap, HashSet, HashTable and ConcurrentHashMap.
From the third edition of Effective Java

Conclusion: if you override equals without overriding hashCode, it and hash set will not work properly.

In that case, let's take our most familiar HashMap for demonstration and push it to. We know that the keys in HashMap cannot be repeated. If they are added repeatedly, the added keys will overwrite the previous contents. Let's see how HashMap determines the uniqueness of keys.

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

Looking at the code, it is found that it determines the storage location in the linked list by calculating the hashCode value of the Map key. In this way, it can be inferred that if we override equals but not hashCode, there may be a contradiction of element duplication.

Let's demonstrate

public class Employee {

private String name;

private Integer age;

public Employee(String name, Integer age) {
    this.name = name;
    this.age = age;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Employee employee = (Employee) o;
    return Objects.equals(name, employee.name) &&
            Objects.equals(age, employee.age);
}

/*@Override
public int hashCode() {
    return Objects.hash(name, age);
}*/
}
public static void main(String[] args) {

    Employee employee1 = new Employee("Ice peak", 20);
    Employee employee2 = new Employee("Ice peak", 22);
    Employee employee3 = new Employee("Ice peak", 20);

    HashMap<Employee, Object> map = new HashMap<>();

    map.put(employee1, "1");
    map.put(employee2, "1");
    map.put(employee3, "1");

    System.out.println("equals: " + employee1.equals(employee3));
    System.out.println("hashCode: " + (employee1.hashCode() == employee3.hashCode()));
    System.out.println(JSONObject.toJSONString(map));
}

Presumably, there are two elements in the value of the map, employee2 and employee3.

results of enforcement

The reason for this problem is that the hashCode is not rewritten, so that when the map calculates the hash value of the key, objects with the same absolute value are calculated except for inconsistent hash values.

Next, open the comment code of hashCode to see the execution result

summary

If equals is rewritten, hashCode must be rewritten. If it is not rewritten, it will cause conflicts with hash sets (HashMap, HashSet, HashTable, ConcurrentHashMap).

Posted by newbie9999 on Mon, 06 Dec 2021 23:56:15 -0800