What Happens If You Override hashCode() But Not equals()?
If you override only hashCode(), objects with the same hash land in the same bucket, but HashMap still considers them different — because equals() by default compares object ref...
🟢 Junior Level
If you override only hashCode(), objects with the same hash land in the same bucket, but HashMap still considers them different — because equals() by default compares object references (==).
What happens:
public class User {
private Long id;
@Override
public int hashCode() { return id != null ? id.intValue() : 0; }
// equals() NOT overridden — Object.equals() is used (reference comparison)
}
Map<User, String> map = new HashMap<>();
map.put(new User(1L), "Alice"); // hashCode=1, bucket #1 → equals not called (bucket empty) → OK
map.put(new User(1L), "Bob"); // hashCode=1, bucket #1 → equals(Alice, Bob) → false (Object.equals = reference comparison!) → new element
System.out.println(map.size()); // 2! Two "identical" keys
Main rule: If you override hashCode — override equals too!
🟡 Middle Level
Practical Consequences
1. Duplicates in HashMap:
- Both
User(id=1)land in the same bucket (hashCode = 100) - On
equals()check, references are compared → different objects → different keys - HashMap allows both
2. Impossible data retrieval:
- Put
new User(1L)→ try to get bynew User(1L) - hashCode is correct (same bucket)
equals()returnsfalse(different references)- Result:
null
3. Increased memory consumption:
- HashSet contains more elements than expected. With high data volume, this leads to increased memory consumption and potential OutOfMemoryError.
Comparison of Two Situations
| Situation | Where searched? | Result |
|---|---|---|
| equals OK, hashCode NO | Different buckets | Not found (wrong place) |
| hashCode OK, equals NO | Same bucket | Not found (not recognized) |
Performance Consequences
Same hashCode for all keys → all elements in one bucket → degradation to O(n)/O(log n). Plus pointless equals() calls.
🔴 Senior Level
Internal Mechanics
On map.get(new User(1L)):
// 1. Find bucket (correct): index = (n-1) & hash = 100
// 2. Iterate elements in bucket 100
// 3. For each: p.hash == hash (true) → p.key.equals(key)
// 4. Object.equals(): this == obj → false (different objects)
// 5. Return null
The element is physically in the correct bucket, but not recognized.
Memory Leak in Production
Set<Event> uniqueEvents = new HashSet<>();
// hashCode overridden by eventId, equals — not
for (Event e : stream) {
uniqueEvents.add(e); // Every object is added!
}
// Result: OOM when processing a large stream
Comparison with Object.equals()
// Object.equals() — simply:
public boolean equals(Object obj) { return this == obj; }
This compares references, not content. Even two new User(1L) — different references → different objects.
Edge Cases
- Singleton-like objects: If the same instance is always used, the problem won’t manifest (but it’s a fragile solution)
- Deserialization: On deserialization, a new object is always created — the problem is guaranteed
Production Diagnostics
Signs of the problem:
- Growing collection size without expected growth
containsKey()returnsfalsefor “existing” keys- Heap dump shows many “identical” objects in Map
Best Practices
- Always generate the pair — use IDE or Lombok
@EqualsAndHashCode - Records — automatically generate both methods
- Static analysis — SonarQube/SpotBugs finds this violation
🎯 Interview Cheat Sheet
Must know:
- Without equals(), objects with the same hashCode land in the same bucket, but Object.equals = reference comparison (==)
- HashMap allows duplicates: two new User(1L) = different keys
- get() returns null — correct bucket, but equals doesn’t recognize the equal object
- OOM when processing streams: HashSet adds every object instead of deduplicating
- Object.equals() = just
this == obj, reference comparison
Common follow-up questions:
- How does it differ from “equals OK, hashCode NO”? — there, search is in the wrong bucket; here, in the correct one, but not recognized
- When won’t the problem manifest? — if the same instance is always used (singleton)
- Why OOM? — uniqueEvents.add() adds every object, Set grows infinitely
- Does deserialization make it worse? — yes, always creates a new object — problem guaranteed
Red flags (DO NOT say):
- “This is a less serious error than missing hashCode” — no, both are critical
- “equals doesn’t need to be overridden for immutable objects” — no, even immutable objects are compared by reference
- “This doesn’t occur in production” — it does, on deserialization
Related topics:
- [[07. What is the equals() and hashCode() Contract]]
- [[10. What Happens If You Override equals() But Not hashCode()]]