In this tutorial, you’ll learn everything about equals() and hashCode() in Java — what they are, why we override them, and how they impact HashMap and other collection classes.
We’ll also explore how HashMap works internally using these two methods and cover important interview questions with examples.
1. Why Do We Need equals() and hashCode()?
When you store objects in a collection like HashSet or HashMap, Java needs a way to determine if two objects are equal.
That’s where equals() and hashCode() come in:
equals()→ Checks if two objects are logically equal.hashCode()→ Returns a hash value (integer) that represents the object’s memory bucket for quick access.
By default, both are inherited from Object class:
public boolean equals(Object obj);
public int hashCode();
2. The Contract Between equals() and hashCode()
Whenever you override equals(), you must also override hashCode().
This ensures consistent behavior inside hash-based collections like HashMap and HashSet.
Contract Rules:
- If two objects are equal (
equals()returns true), theirhashCode()must be the same. - If two objects have the same
hashCode(), they may or may not be equal. - If
equals()returns false,hashCode()can be same or different.
3. Example Without Overriding equals() and hashCode()
class Student {
int id;
String name;
Student(int id, String name) {
this.id = id;
this.name = name;
}
}
public class WithoutOverrideExample {
public static void main(String[] args) {
Student s1 = new Student(101, "Ravi");
Student s2 = new Student(101, "Ravi");
System.out.println(s1.equals(s2)); // false
}
}
Output: false
👉 Even though both objects look identical, equals() from Object compares memory addresses — so they’re not equal.
4. Example With equals() and hashCode() Overridden
class Student {
int id;
String name;
Student(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return id == student.id && name.equals(student.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
public class WithOverrideExample {
public static void main(String[] args) {
Student s1 = new Student(101, "Ravi");
Student s2 = new Student(101, "Ravi");
System.out.println(s1.equals(s2)); // true
System.out.println(s1.hashCode() == s2.hashCode()); // true
}
}
Output:
true
true
✅ Now both objects are logically equal and produce the same hash code.
5. equals() and hashCode() in Real-World: HashMap Example
When you store objects as keys in a HashMap, Java uses both methods internally to find or update entries.
class Employee {
int id;
String name;
Employee(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Employee)) return false;
Employee emp = (Employee) o;
return id == emp.id && name.equals(emp.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
public class HashMapExample {
public static void main(String[] args) {
Map<Employee, String> map = new HashMap<>();
Employee e1 = new Employee(1, "Amit");
Employee e2 = new Employee(1, "Amit");
map.put(e1, "Developer");
map.put(e2, "Tester");
System.out.println(map.size()); // 1
System.out.println(map.get(e1)); // Tester
}
}
Output:
1
Tester
✅ Because both keys are logically equal (same id and name), HashMap replaces the old value.
6. How HashMap Works Internally (Step-by-Step)
Understanding how HashMap uses hashCode() and equals() internally is one of the most common interview questions.
Let’s break it down:
Step 1: Compute hash code
When you call put(key, value), Java first calls hashCode() on the key object.
Step 2: Find bucket index
It calculates a bucket index using a hashing algorithm:
index = hash(key.hashCode()) % capacity
Step 3: Check for existing key
If the bucket is empty → insert new node.
If not empty → compare existing key using equals().
Step 4: Collision handling
If two keys have the same hash code, HashMap stores them in the same bucket using a linked list or a tree (since Java 8).
Step 5: Treeification
When a bucket’s size exceeds 8 elements, it converts the linked list into a balanced tree to improve lookup performance.
Step 6: get(key) operation
When you call map.get(key), Java again uses hashCode() to locate the bucket and equals() to find the exact match.
7. Visual Representation of HashMap Working
Put(e1, "Developer")
├─ hashCode() → 123
├─ Bucket[123]
└─ stores Node(key=e1, value="Developer")
Put(e2, "Tester")
├─ hashCode() → 123 (same)
├─ Checks equals(e1, e2) → true
└─ Replaces "Developer" with "Tester"
✅ That’s why overriding both methods correctly is crucial when using objects as keys.
8. Common Mistakes Developers Make
- Overriding
equals()but nothashCode(). - Using mutable fields (like
name) in hash calculation — changing them breaks retrieval. - Assuming same hash code means equal objects — not always true.
- Using identity-based equality when logical equality is needed.
9. Interview Questions and Answers
- Q: What is the difference between equals() and hashCode()?
A: equals() checks logical equality; hashCode() provides the hash bucket location. - Q: What happens if you override equals() but not hashCode()?
A: Hash-based collections may treat equal objects as different keys. - Q: Can two unequal objects have the same hashCode()?
A: Yes, that’s called a collision. - Q: What data structures use equals() and hashCode()?
A: HashMap, HashSet, LinkedHashMap, ConcurrentHashMap. - Q: Why should fields used in equals() also be used in hashCode()?
A: To maintain consistency — both should define equality in the same way. - Q: What happens internally when you put a key-value pair in a HashMap?
A: HashMap calculates hash → finds bucket → uses equals() for match → replaces or adds new node. - Q: What is a hash collision?
A: When two keys have the same hash code but are not equal. - Q: What is treeification in HashMap?
A: When a bucket’s linked list becomes longer than 8 nodes, it’s converted into a red-black tree. - Q: Why are HashMap keys immutable?
- A: Changing key values changes hashCode(), breaking retrieval.
- Q: Is HashMap thread-safe
A: No. UseConcurrentHashMapfor thread-safe operations.
10. Summary Table
| Aspect | equals() | hashCode() |
|---|---|---|
| Purpose | Logical equality | Bucket identification |
| Return Type | boolean | int |
| Default Behavior | Compares memory addresses | Generates unique hash based on memory |
| Override Required | Yes (for custom equality) | Yes (if equals overridden) |
| Used In | Comparing objects | HashMap, HashSet lookup |
Conclusion
🎯 In summary, equals() and hashCode() form the foundation of Java’s hashing-based data structures.
If you override one, always override the other to maintain consistency and ensure correct behavior in collections like HashMap and HashSet.
Next, you can explore “How HashSet works internally” — which follows the same principles under the hood.
0 Comments