Overriding equals() and hashcode()

0. Introduction
When discussing HashMap in Java interviews, a very common follow-up question appears after explaining hash collisions:
"Why do we need to override both equals() and hashCode()?"
However, an important point is that the rule about overriding both methods is not specific to HashMap. It is a general contract defined by Java for all classes.
Whenever objects are compared for logical equality, the relationship between equals() and hashCode() must remain consistent.
In practice, this rule becomes especially visible when working with hash-based collections such as:
HashMapHashSetHashtable
These data structures rely on both methods to function correctly.
In this article, we use HashMap as a concrete example to illustrate why the contract between equals() and hashCode() matters and how it affects real behavior in collections.
We will cover:
The role of
hashCode()The role of
equals()How
HashMapuses both during lookupWhy both methods must be overridden together
1. Role of hashCode()
The hashCode() method determines which bucket an object belongs to inside a HashMap.
When inserting a key-value pair:
map.put(key, value);
The following steps occur.
2.1. Compute the hash
Java first calls:
key.hashCode()
This produces an integer representing the object's hash value.
2.2. Determine the bucket index
The hash value is used to determine the index in the internal array.
index = (n - 1) & hash
This calculation decides which bucket the entry should be stored in.
Therefore, hashCode() is responsible for bucket selection.
2. Role of equals()
hashCode() determines which bucket a key belongs to, while equals() determines whether two keys are logically equal inside that bucket.
This becomes important when multiple keys map to the same bucket.
3.1. Collision scenario
Example:
key1.hashCode() -> bucket 3
key2.hashCode() -> bucket 3
Both keys are stored in the same bucket.
Inside that bucket, HashMap must determine which entry matches the requested key.
3.2. Key comparison
During lookup:
map.get(key)
HashMap:
Computes the hash
Finds the correct bucket
Traverses nodes in that bucket
Uses
equals()to compare keys
Example bucket:
(keyA, valueA) -> (keyB, valueB) -> (keyC, valueC)
equals() determines which key matches.
Therefore, equals() is responsible for key comparison inside a bucket.
3. Why Both Must Be Overridden
Java defines a contract between equals() and hashCode().
4.1. The Contract
If two objects are considered equal:
a.equals(b) == true
Then they must return the same hash code.
a.hashCode() == b.hashCode()
If this rule is violated, HashMap may place the objects in different buckets.
4.2. Example Problem
Consider the following class:
class Person {
String name;
public boolean equals(Object o) {
Person p = (Person) o;
return this.name.equals(p.name);
}
}
Here equals() is overridden but hashCode() is not.
Result:
Two logically equal objects may produce different hash codes.
p1.equals(p2) -> true
p1.hashCode() -> 100
p2.hashCode() -> 245
This causes them to be stored in different buckets.
When searching:
map.get(p2)
HashMap looks in the bucket determined by p2.hashCode() and fails to find the entry inserted with p1.
4. Correct Implementation
To follow the contract, both methods should be implemented together.
class Person {
String name;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person p = (Person) o;
return name.equals(p.name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}
Now two objects with the same name will:
Produce the same hash code
Be placed in the same bucket
Be correctly compared using
equals()
✨ Conclusion
hashCode() determines which bucket a key belongs to, while equals() determines whether two keys are logically equal. HashMap relies on both methods during insertion and lookup. Because of this relationship, overriding equals() without also overriding hashCode() can lead to incorrect behavior where keys cannot be found even though they exist in the map.





