HashMap vs ConcurrentHashMap: The Java Interview Question, Explained Simply
By Preeti Verma
If you interview for a Java role, you will almost certainly be asked about HashMap. Often the next question is how it differs from ConcurrentHashMap. Here is the whole story in plain words.
Think of a HashMap as a wall of lockers
Imagine a wall of numbered lockers. When you store something, a rule looks at your key (the name on the parcel) and works out which locker number to use. When you come back, the same rule points to the same locker, so you do not search the whole wall.
That rule is the hash function, and the lockers are the buckets of an array inside the HashMap.
What happens on put and get
- The key's hashCode() is computed and spread a little so that small differences change the result.
- The result is turned into an index into the bucket array.
- If the bucket is empty, the entry goes in. If not, HashMap compares keys with equals() to see whether it is the same key (update the value) or a different key that landed in the same bucket.
Two different keys in the same bucket is called a collision. It is normal and not a bug. HashMap keeps the colliding entries in a small chain. In modern Java, a long chain in a large enough table is converted into a balanced tree, so even a very crowded bucket stays reasonably fast.
Resizing
A HashMap starts small. When it holds more entries than the load factor allows (0.75 by default), it doubles the array and re-places every entry. That work is why a HashMap can suddenly feel slow at one moment. If you know you will store about 10,000 entries, giving it a sensible initial capacity avoids repeated resizing.
Why HashMap breaks with several threads
HashMap is not thread-safe. If two threads write at the same time, one update can overwrite the other, and a thread can read half-updated structure during a resize. In old Java versions a resize under contention could even cause an infinite loop. In newer versions you are more likely to see lost updates and missing entries, which is worse in a way because nothing crashes.
Your options, and what they cost
- Collections.synchronizedMap: wraps every method in one lock. Safe, simple, and slow under heavy contention because only one thread works at a time.
- Hashtable: the old version of the same idea. Nobody chooses it for new code.
- ConcurrentHashMap: built for concurrency. This is the usual answer.
How ConcurrentHashMap works without one big lock
In Java 7, the map was split into segments, each with its own lock, so threads working on different segments did not block each other.
In Java 8 and later, the design changed. Reads are mostly lock-free. To write, ConcurrentHashMap uses a compare-and-set for an empty bucket, and otherwise takes a lock on just the first node of that one bucket. So threads writing to different buckets never wait for each other.
It also gives you atomic operations such as computeIfAbsent, merge and putIfAbsent. These are the safe way to do "check, then update", which would be a race if you wrote it yourself with get and put.
Why ConcurrentHashMap does not allow null keys or values
This is a favourite follow-up. If get(key) returns null in a normal HashMap, you can call containsKey to learn whether the key is missing or maps to null. In a concurrent map, another thread could change the map between those two calls, so the answer would be unreliable. Banning nulls removes the ambiguity: null from get always means "not present".
Common traps
- Thinking ConcurrentHashMap makes a sequence of operations atomic. Each call is safe, but "get, then put" is still two steps. Use compute or merge.
- Expecting size() to be exact under heavy writes. It is an estimate at the moment you read it.
- Using mutable objects as keys. Change the key after storing it and the hash points to the wrong bucket.
A 30-second interview answer
"HashMap stores entries in an array of buckets, choosing the bucket from the key's hash and using equals to tell keys apart in a collision. It is not thread-safe, so concurrent writes can lose updates. ConcurrentHashMap fixes that with lock-free reads and fine-grained locking on individual buckets, and it offers atomic methods like merge. It disallows null keys and values so that a null from get is never ambiguous."
If you want to practise saying answers like this out loud, the GenQuest AI Coach can produce a 30-second version for any question and explain it again in simpler words if it does not click. For more Java topics, see our guide to Core Java questions for experienced developers.