The most dangerous bugs aren't the ones that crash your application... they're the ones that silently return the wrong result.
If you've been working with Java for a while, you've probably experienced something like this.
Your code compiles.
Your application starts normally.
No exceptions are thrown.
Yet somehow...
map.get(key);returns
nulleven though you just inserted that object into the map.
Sound impossible?
It isn't.
This is one of the most common interview questions - and one of the most expensive production bugs in Java applications.
Today, we'll uncover why HashMap sometimes appears to "forget" your data.
A Real Production Story
A fintech company stored customer sessions inside a HashMap.
Everything worked perfectly during testing.
A few days after deployment...
Random users started getting logged out.
The session clearly existed.
The logs showed it was successfully inserted.
But when retrieving it...
sessionMap.get(session);returned
nullThe infrastructure team spent two days investigating Redis.
The backend team blamed JVM memory.
Someone even suspected garbage collection.
The real culprit?
A single setter.
First, Let's Understand How HashMap Finds Your Object
HashMap does not search every key.
Instead, it performs three steps.
-
Calculate the key's hash code.
-
Find the correct bucket.
-
Compare keys using
equals().
It looks roughly like this.
Key
↓
hashCode()
↓
Bucket Number
↓
equals()
↓
Value
Everything Works...
Imagine this class.
class Employee {
private int id;
Employee(int id) {
this.id = id;
}
public void setId(int id) {
this.id = id;
}
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object obj) {
Employee other = (Employee) obj;
return this.id == other.id;
}
}Now let's use it.
Map<Employee, String> employees = new HashMap<>();
Employee emp = new Employee(101);
employees.put(emp, "Rohit");Everything looks perfectly fine.
Then Someone Changes the Object
Later...
emp.setId(202);Nothing seems wrong.
No exception.
No warning.
No compiler error.
Now try this.
System.out.println(employees.get(emp));Output
nullWait...
It is the same object.
How can HashMap lose it?
The Secret Lies Inside HashMap
When the object was inserted
id = 101HashMap calculated
hashCode = 101Suppose it stored the object inside
Bucket 5After changing
id = 202;the object's hash code also changed.
Now HashMap calculates
hashCode = 202which points to
Bucket 12But your object is still physically stored inside
Bucket 5HashMap searches Bucket 12.
The object lives in Bucket 5.
Result?
null
Visual Representation
|
Before Mutation |
After Mutation |
|---|---|
|
Employee(101) |
Employee(202) |
|
hash = 101 |
hash = 202 |
|
Bucket 5 |
Bucket 12 |
|
Object stored in Bucket 5 |
Object still stored in Bucket 5 |
|
Lookup succeeds |
Lookup fails |
Why Doesn't HashMap Automatically Move It?
Because HashMap has no idea that your object's internal state changed.
It only performs rehashing when:
-
resizing
-
rebuilding buckets
-
inserting new entries during resize
It never watches your object's fields.
Doing so would make every lookup incredibly slow.
The Interview Twist
Many interviewers ask this question.
Can HashMap keys be mutable?
The technically correct answer is
Yes.
But they should never be mutable if those mutable fields participate in equals() or hashCode().
That's the important distinction.
Another Hidden Bug
Imagine this.
HashSet<Employee> employees = new HashSet<>();
Employee emp = new Employee(10);
employees.add(emp);
emp.setId(20);
System.out.println(employees.contains(emp));Output
falseSame root cause.
HashSet internally uses...
HashMap.
Why String Makes a Perfect HashMap Key
Have you ever wondered why Java developers frequently use
Map<String, Object>instead of
Map<Employee, Object>Because String is immutable.
Once created,
String name = "Java";it can never change.
Its hash code never changes.
Its bucket never changes.
Its lookup always works.
That's one of the major reasons Java made String immutable.
Best Practices
Always follow these rules.
-
Use immutable keys.
-
Never modify fields used in
equals(). -
Never modify fields used in
hashCode(). -
Override
equals()andhashCode()together. -
Prefer records or immutable objects as map keys.
-
Be extra careful when using Lombok's generated methods.
Production Impact
This bug can lead to:
-
Missing cache entries
-
Failed authentication
-
Duplicate records
-
Memory leaks
-
Incorrect business logic
-
Random production failures
-
Extremely difficult debugging sessions
The scary part?
No exception is ever thrown.
Common Interview Questions
Why does HashMap fail after modifying a key?
Because changing fields involved in hashCode() changes the computed bucket, but the entry remains stored in the original bucket.
Does HashMap move the object automatically?
No.
Does this happen in HashSet?
Yes, HashSet is backed by a HashMap.
Is this bug detectable by the compiler?
No, The code compiles successfully.
What's the safest key type?
-
String -
Java Records
-
Immutable custom objects
-
Wrapper classes like
Integer,Long, andUUID
Key Takeaways
HashMap doesn't lose your object.
You changed the address where it tries to find it.
Understanding this single concept separates developers who use Java collections from those who truly understand how they work internally.
The next time someone says:
"HashMap randomly stopped working."
You'll know exactly where to look.
Recommended Reading
Enjoyed this article?
If you're preparing for Java interviews, aiming for Senior Software Engineer roles, or looking to strengthen your understanding of Java, Spring Boot, System Design, and Backend Development, I'd be happy to help.
👉 Book a 1:1 Career Mentorship Session or Mock Interview with me:
Let's work together to identify your knowledge gaps, improve your interview performance, and accelerate your software engineering career.

Discussion