Explain the equals() and hashCode() contract.

9 minintermediateequalshashCodecontract

Quick Answer

If two objects are equal per equals(), they must return the same hashCode(). The reverse isn't required — unequal objects may share a hash (a collision). Violating the contract breaks hash-based collections (HashMap, HashSet), since lookups first compare buckets by hash and only then confirm with equals().

Detailed Answer

The contract, defined on Object, has three parts:

  1. Consistency: repeated calls to hashCode() return the same value as long as no field used in equals() changes.
  2. Equal implies equal hash: if a.equals(b) is true, then a.hashCode() == b.hashCode() must be true.
  3. Unequal doesn't require different hash: two unequal objects may share a hash code (a collision) — that's allowed and expected, hash-based structures handle it.

Why it matters: HashMap/HashSet locate a bucket using hashCode(), then use equals() to distinguish entries within that bucket. If you override equals() but not hashCode(), two "equal" objects can land in different buckets — a HashSet might contain what look like duplicates, and map.get(key) can fail to find an entry you just put in.

public class Point {
    private final int x, y;
    // ...
    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Point p)) return false;
        return x == p.x && y == p.y;
    }
    @Override
    public int hashCode() {
        return Objects.hash(x, y); // must use the same fields as equals()
    }
}

Modern Java: record types generate a correct equals()/hashCode()/toString() automatically based on their components, which is one of their main appeals for simple data carriers.

Related Resources