Mastery
Mastery/Java/A. Object model, identity & memory
T1 · high-leverage

The hashCode/equals contract

Hash-based collections (HashSet, HashMap) find an entry's bucket using hashCode(), then confirm the match using equals(). If you override equals() without also overriding hashCode(), two "equal" objects can land in different buckets — so a HashSet will silently contains() == false for an object it should logically consider a duplicate. The class below deliberately overrides only equals() to show the break.

java
import java.util.*;

class Bad {
    int v;
    Bad(int v) { this.v = v; }
    @Override public boolean equals(Object o) { return o instanceof Bad b && b.v == v; }
    // no hashCode() override -> falls back to identity hash, contract broken
}

Set<Bad> set = new HashSet<>();
Bad b1 = new Bad(5);
set.add(b1);

System.out.println("contains(new Bad(5)) [equal by value]: " + set.contains(new Bad(5)));
System.out.println("contains(b1) [same reference]:         " + set.contains(b1));

Interview angle

A must-know for any Java role beyond entry level. Interviewers use it to check whether a candidate understands why the contract exists — how hash-based lookup actually finds a bucket, then confirms the match — rather than just knowing that IDEs auto-generate both methods together. That "why" is what lets someone diagnose a broken contract in code they didn't write.

In the industry

IDE-generated implementations (IntelliJ, Eclipse) and Lombok's @EqualsAndHashCode exist specifically because hand-written violations of this contract were such a common, hard-to-debug source of "why isn't my object found in this HashSet" bugs. Even so, custom equals-only overrides — a frequent mistake on JPA entity classes in particular — remain a recurring real-world defect that surfaces as objects mysteriously "disappearing" from hash-based collections.