Explain the equals() and hashCode() contract.
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:
- Consistency: repeated calls to
hashCode()return the same value as long as no field used inequals()changes. - Equal implies equal hash: if
a.equals(b)istrue, thena.hashCode() == b.hashCode()must be true. - 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.