Java Collections + Hashing
How does HashMap work internally?
HashMap stores key-value pairs in buckets. It uses the key's hash code to find a bucket, handles collisions inside that bucket, and resizes when the map becomes too full.
The Short Answer
A HashMap stores key-value pairs in an internal array of buckets.
When you call put(key, value), Java uses the key's hash code to decide which bucket the entry should go into. When you call get(key), Java uses the same idea to quickly jump to the likely bucket instead of scanning every entry.
The Mental Model
Key → hashCode → bucket index
Key
"user:42"
hashCode()
numeric hash
bucket index
array slot
What Happens During put()?
- HashMap receives the key and value.
- It calculates a hash from the key.
- It converts that hash into a bucket index.
- If the bucket is empty, it stores the entry there.
- If the bucket already has entries, it checks whether the key already exists using
equals(). - If the key exists, the old value is replaced. If not, a new entry is added to that bucket.
Map<String, Integer> scores = new HashMap<>();
scores.put("alice", 90);
scores.put("bob", 85);
scores.put("alice", 95); // replaces Alice's old valueWhy hashCode() and equals() Matter Together
HashMap uses hashCode() to find the bucket, but it uses equals() to confirm whether two keys are actually the same logical key.
hashCode() helps find the right neighborhood. equals() identifies the exact house.class UserId {
private final String value;
UserId(String value) {
this.value = value;
}
// If two UserId objects are logically equal,
// equals() and hashCode() must agree.
}What Is a Collision?
A collision happens when two different keys land in the same bucket. This is normal. HashMap is designed to handle it.
Two keys, same bucket
If collisions are rare, HashMap remains very fast. If many keys collide into the same bucket, lookup becomes slower because Java has to search within that bucket.
Linked List vs Tree Bin
In older mental models, people often say: “A HashMap bucket contains a linked list.” That is partly true, but modern Java has an extra optimization.
If too many entries collide into the same bucket, Java can convert that bucket from a linked structure into a tree structure. This improves worst-case lookup behavior when collisions are heavy.
Normal Collision Handling
Heavy Collision Case
Why Resizing Happens
HashMap has a capacity, which is the number of buckets. It also has a load factor, which controls how full the map is allowed to get before it grows.
The common default load factor is 0.75. So if the capacity is 16, resizing happens after the number of entries exceeds 12.
capacity = 16
loadFactor = 0.75
threshold = capacity * loadFactor
threshold = 16 * 0.75 = 12When the threshold is exceeded, HashMap creates a larger internal table and redistributes entries into new buckets.
Resizing Visualized
Before Resize
Buckets are getting crowded. Collisions become more likely.
After Resize
More buckets reduce crowding, but resizing itself has a cost.
The Interview-Friendly Explanation
Common Interview Follow-Ups
Why is HashMap usually O(1)?
Because hashing lets Java jump directly to a bucket instead of scanning every entry. This assumes the hash function distributes keys well.
What happens if many keys have the same hash?
They collide into the same bucket. Lookup becomes slower because Java must search within that bucket.
Why must equals() and hashCode() be consistent?
If two objects are equal, they must produce the same hash code. Otherwise, HashMap may look in the wrong bucket and fail to find a logically equal key.
Why is resizing expensive?
Because HashMap must allocate a larger internal table and redistribute entries into new bucket positions.