Why Overriding equals Requires Overriding hashCode: An Interview‑Ready Guide
The article explains the contract between equals and hashCode in Java, shows what goes wrong when only one is overridden, demonstrates the correct implementation with Objects.equals and Objects.hash, covers IDE/Lombok generation, JPA entity nuances, inheritance pitfalls, records, and common anti‑patterns.
1. What the default equals does
Object.equals simply checks reference equality ( return this == obj;), so two distinct instances with identical fields are not considered equal.
public boolean equals(Object obj) {
return (this == obj);
}Therefore we usually override equals to define business‑level equality, e.g. comparing the id field of a User object.
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User u)) return false;
return Objects.equals(id, u.id);
}2. Why hashCode must be overridden together
The JDK contract states that if two objects are equal according to equals, they must return the same hashCode. Violating this breaks collections that rely on hashing.
Example with only equals overridden:
User u1 = new User(1L, "Tom");
User u2 = new User(1L, "Tom");
Set<User> set = new HashSet<>();
set.add(u1);
set.contains(u2); // expected true, but returns falseHashSet/HashMap first locate a bucket using hashCode; different hash codes cause the collection to treat the objects as unrelated.
3. Standard way to override both
Pick the fields that represent the business identity (usually id) and use Objects.equals and Objects.hash:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User u)) return false;
return Objects.equals(id, u.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}Rule of thumb: the fields used in equals must be the same fields used in hashCode.
4. IDE / Lombok one‑click generation
IDEA can generate both methods (Cmd/Ctrl + N → equals() and hashCode()). Lombok annotations also help:
@Data // generates equals/hashCode for all fields
public class User { ... }
@EqualsAndHashCode(of = "id")
public class User { ... }
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class User {
@EqualsAndHashCode.Include
private Long id;
private String name;
}While Lombok is convenient, developers must understand the underlying rules to avoid misuse.
5. Special considerations for JPA entities
Using @Data on a JPA entity can cause problems:
All fields participate in equals/hashCode, triggering lazy‑loaded associations and causing performance issues.
Bidirectional relationships may lead to infinite recursion and StackOverflowError.
Auto‑generated IDs are null before persistence, making equals behave unexpectedly.
Recommended approach:
@Entity
@EqualsAndHashCode(of = "id")
public class User {
@Id
private Long id;
// other fields
}Only compare id after it is persisted; otherwise, two transient objects with id == null would be considered equal.
6. Inheritance and symmetry
When a subclass overrides equals using instanceof, symmetry can break:
public class A { int x; }
public class B extends A { int y; }
A a = new A(); a.x = 1;
B b = new B(); b.x = 1; b.y = 2;
a.equals(b); // may return true
b.equals(a); // returns falseThis violates the contract. Solutions include making classes final or using getClass() == o.getClass() for strict type equality (at the cost of Liskov substitution).
7. Records (Java 14+)
Records automatically generate equals and hashCode based on all components, satisfying the contract and being immutable: public record Point(int x, int y) {} For simple data carriers, records are preferred; manual equals implementations are largely unnecessary.
8. Common anti‑patterns
Only overriding equals : HashSet/HashMap behave incorrectly.
hashCode returning a constant : All entries land in the same bucket, degrading performance to O(n) or O(log n) after treeification.
Throwing exceptions in equals : Must check type with instanceof before casting.
Using mutable fields in hashCode : Changing a field after insertion changes the hash, causing look‑ups to fail.
9. Summary
Always override hashCode when you override equals.
Use Objects.equals and Objects.hash; IDEs and Lombok can generate them.
Select business‑identity fields (typically id) for both methods.
For JPA entities, limit equality to the primary key and handle id == null cases.
Prefer records for immutable data carriers.
Keep fields used in hashCode immutable, or avoid mutating objects after they are placed in hash‑based collections.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
