9. The equals() / hashCode() Contract
equals() and hashCode() must be overridden together and satisfy a strict contract; violating it silently breaks HashMap, HashSet, and all hash-based data structures.
Explanation
The contract: if two objects are equal according to equals(), they must have the same hashCode(). The converse is not required โ unequal objects may share a hash code (collision). If you override one without the other, hash-based collections break silently.
For example, if you override equals() but not hashCode(), two "equal" objects will hash to different buckets in a HashMap and appear as separate entries. You'll put one and never find it with a get using the equal key.
Modern Java: use java.util.Objects.equals() (null-safe) and java.util.Objects.hash() in your implementations, or let the IDE generate them. Better still, use records (Java 16+) โ they automatically provide correct equals(), hashCode(), and toString() based on all components.
Example
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class EqualsHashCode {
// BAD: only equals, no hashCode
static class BadKey {
final String value;
BadKey(String v) { this.value = v; }
@Override public boolean equals(Object o) {
return o instanceof BadKey b && value.equals(b.value);
}
// No hashCode override โ BROKEN for HashMap!
}
// GOOD: both overridden correctly
static class GoodKey {
final String value;
GoodKey(String v) { this.value = v; }
@Override public boolean equals(Object o) {
return o instanceof GoodKey g && Objects.equals(value, g.value);
}
@Override public int hashCode() {
return Objects.hash(value);
}
}
// BEST in Java 16+: record does it automatically
record RecordKey(String value) {}
public static void main(String[] args) {
Map<BadKey, String> bad = new HashMap<>();
bad.put(new BadKey("test"), "hello");
System.out.println(bad.get(new BadKey("test"))); // null! (wrong bucket)
Map<GoodKey, String> good = new HashMap<>();
good.put(new GoodKey("test"), "hello");
System.out.println(good.get(new GoodKey("test"))); // "hello" โ
Map<RecordKey, String> rec = new HashMap<>();
rec.put(new RecordKey("test"), "hello");
System.out.println(rec.get(new RecordKey("test"))); // "hello" โ
}
}Sources
- https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Object.html#hashCode() โ Object.hashCode() contract
- https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Objects.html โ Objects utility class