equals and hashCode: The Contract Explained
equals and hashCode: The Contract Explained
Every Java object inherits equals and hashCode from Object. Overriding them correctly is the difference between working hash collections and baffling bugs. Hash-based collections like HashSet, HashMap, and ConcurrentHashMap rely on both methods, so getting one wrong breaks all of them at once.
The equals contract
equals must be reflexive, symmetric, transitive, and consistent, and equals(null) must return false. In practice, override it for value objects where two instances with the same fields should be considered equal, such as an entity's natural key or a DTO you compare in tests.
The hashCode contract
The rule is simple: if two objects are equal according to equals, they must have the same hashCode. The reverse is not required, which is why collisions are normal. If you violate this rule, a HashMap will fail to find keys you inserted, because it first compares hash buckets before calling equals.
Writing them correctly
Use Objects.equals and Objects.hash to keep the code short and correct, even when fields can be null. This pattern is battle-tested and easy to read.
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return Objects.equals(title, book.title)
&& Objects.equals(author, book.author);
}
@Override
public int hashCode() {
return Objects.hash(title, author);
}
Common pitfalls
Never include mutable fields in hashCode, or the object's bucket changes while it sits in a set. Never base equality on identity when value semantics are intended. And do not forget that records handle this for you: a record gets a pristine equals and hashCode automatically, so prefer records for immutable data carriers.
Key Points
- Equal objects must produce the same hashCode, or hash collections misbehave.
equalsmust be reflexive, symmetric, transitive, and null-safe.- Use
Objects.equalsandObjects.hashfor safe, concise implementations. - Use only immutable fields in
hashCode. - Records provide correct implementations automatically.