The methods equals()
and hashCode()
play a critical role in how HashMap
works under the hood.
Let’s break it down with a clear and intuitive explanation. 🧠
🔧 How HashMap stores and finds keys:
When you do:
map.put(key, value);
Step 1: Use hashCode()
to find the bucket index
int hash = key.hashCode();
int index = hash % table.length;
➡️ This determines which bucket (array slot) the entry goes into.
Step 2: Use equals()
to find the exact key within that bucket
Buckets can have collisions (multiple keys in one bucket). So inside the bucket, HashMap loops through entries like:
if (existingKey.equals(newKey)) {
// key already exists → overwrite value
}
🎯 So, what’s the role of each?
Method | Role in HashMap |
---|---|
hashCode() | Locates the bucket in the internal array |
equals() | Locates the exact key within the bucket |
⚠️ Why both are needed?
If you only override hashCode()
but not equals()
, HashMap might:
- Find the right bucket, but
- Fail to recognize an existing key,
- Add duplicates or miss lookups.
If you only override equals()
but not hashCode()
, keys that should be equal may:
- End up in different buckets,
- Become unreachable (i.e. lost).
🧪 Example (bad key class):
class BadKey {
int id;
public boolean equals(Object o) {
return o instanceof BadKey && this.id == ((BadKey)o).id;
}
// ❌ No hashCode()
}
Map<BadKey, String> map = new HashMap<>();
map.put(new BadKey(1), "value");
map.get(new BadKey(1)); // returns null ❌
✅ Example (correct key class):
class GoodKey {
int id;
public boolean equals(Object o) {
return o instanceof GoodKey && this.id == ((GoodKey)o).id;
}
public int hashCode() {
return Objects.hash(id);
}
}
Now, everything works:
map.put(new GoodKey(1), "value");
map.get(new GoodKey(1)); // returns "value" ✅
TL;DR
hashCode()
→ finds the bucketequals()
→ finds the right key in the bucket- Both are essential for correct behavior of
HashMap