HashMap Internal Working in Java: What Happens When You Execute map.put(“Java”, 100)?
HashMap is one of the most frequently asked topics in Java interviews. Almost every interviewer asks:
“Can you explain what happens internally when we execute
map.put("Java", 100)?”
Understanding the internal working of HashMap will help you answer confidently in interviews and write better Java code.
Let’s understand each step in detail.
Example
HashMap<String, Integer> map = new HashMap<>();
map.put("Java", 100);
When the put() method is called, HashMap performs the following operations internally.
Step 1: Calculate the Hash
The first thing HashMap does is calculate the hash value of the key.
"Java".hashCode();
For a String, Java computes a unique integer hash code based on the characters in the string.
For example:
"Java".hashCode() = 2301506
(Actual value isn’t important; it’s just an integer.)
Instead of using the hash directly, HashMap applies an additional bit-mixing operation to distribute keys more evenly.
Internally, Java performs something similar to:
hash = key.hashCode() ^ (key.hashCode() >>> 16);
Why is bit mixing needed?
Without bit mixing, many keys could end up in the same bucket, increasing collisions.
Bit mixing improves the distribution of keys across buckets, resulting in faster lookups.
Step 2: Find the Bucket Index
Once the hash is calculated, HashMap determines where the entry should be stored.
It calculates the bucket index using:
index = (n - 1) & hash;
where:
- n = current table size (capacity)
- hash = hash value calculated in Step 1
Suppose the table size is:
Capacity = 16
Then:
index = (16 - 1) & hash
The calculated index might be:
Bucket = 5
So HashMap now knows that the key should be stored in Bucket 5.
Step 3: Check the Bucket
Now HashMap checks whether the bucket is empty.
Suppose the bucket looks like this:
Bucket 5
Empty
Since nothing exists there, HashMap creates a new node.
Internally, something similar to this happens:
new Node(hash, "Java", 100, null)
The bucket now becomes:
Bucket 5
[Java,100]
The insertion is complete.
What if the Bucket is Already Occupied?
Now consider another insertion:
map.put("Python", 200);
Assume "Python" also maps to Bucket 5.
The bucket already contains:
Bucket 5
[Java,100]
Since the bucket is not empty, HashMap cannot directly insert the new node.
Instead, it compares the keys.
Internally:
existingKey.equals(newKey)
becomes
"Java".equals("Python")
Result:
false
The keys are different, so this is a collision.
Step 4: Handle Collision
A collision occurs when two different keys map to the same bucket.
There are two possible cases.
Case 1: Same Key → Update the Value
Suppose you execute:
map.put("Java", 300);
The bucket contains:
[Java,100]
HashMap compares:
"Java".equals("Java")
Result:
true
Since the key already exists, HashMap does not create a new node.
Instead, it updates the existing value.
Before:
[Java,100]
After:
[Java,300]
The old value (100) is replaced by the new value (300).
Case 2: Different Key → Create a New Node
Now consider:
map.put("Python", 200);
The bucket already contains:
[Java,100]
Comparison:
"Java".equals("Python")
Result:
false
Since the keys are different, HashMap creates a new node and links it to the existing one.
Bucket 5
[Java,100]
|
v
[Python,200]
This linked structure is how HashMap handles collisions.
What Happens if Many Keys Collide?
Suppose many keys map to the same bucket.
Bucket 5
Java
|
Python
|
Spring
|
Hibernate
|
Docker
|
Kubernetes
Searching through a long linked list becomes slow.
Before Java 8, buckets always remained linked lists.
Starting with Java 8, if:
- Bucket size becomes greater than or equal to 8, and
- Table capacity is at least 64
then the linked list is converted into a Red-Black Tree.
Java
/ \
Docker Python
/ \
Spring Kubernetes
A Red-Black Tree reduces search complexity from:
O(n)
to
O(log n)
making lookups much faster when many collisions occur.
Step 5: Check the Load Factor
After inserting the new node, HashMap checks whether resizing is required.
By default:
Capacity = 16
Load Factor = 0.75
Threshold:
16 × 0.75 = 12
This means the table can hold 12 entries before resizing.
When the 13th entry is inserted:
size = 13
Since:
13 > 12
HashMap resizes the table.
Old Capacity = 16
New Capacity = 32
After resizing, every existing entry is recalculated and placed into its new bucket.
This process is called rehashing.
Complete Flow of put()
map.put("Java",100)
|
v
Calculate hash
|
v
Find bucket index
|
v
Is bucket empty?
/ \
Yes No
| |
Create Node Compare keys
| |
Store Node equals() ?
/ \
true false
| |
Update value Add new Node
|
v
Linked List / Tree
|
v
Check Load Factor
|
v
Resize if threshold exceeded
Time Complexity
| Operation | Average | Worst Case |
|---|---|---|
| put() | O(1) | O(n) |
| get() | O(1) | O(n) |
| remove() | O(1) | O(n) |
With treeification (Java 8+), the worst-case lookup in a heavily-collided bucket improves to O(log n).
Interview Answer (30 Seconds)
When
map.put("Java", 100)is executed,HashMapfirst calculates the hash of the key and applies bit mixing. It then computes the bucket index using(n - 1) & hash. If the bucket is empty, it creates a new node and stores it. If the bucket already contains entries, it compares keys usingequals(). If the key matches, it updates the existing value; otherwise, it adds a new node to the bucket. If too many collisions occur, the linked list is converted into a Red-Black Tree in Java 8+. Finally, if the number of entries exceedscapacity × loadFactor(default0.75), the table is resized and all entries are rehashed.