← Home
🏗Crack System Design with DSA AnimatorSystem DesignNEW
🏗 System Design›Concurrency & Threading

⚙️Internal Working: HashMap vs ConcurrentHashMap

Same hash-bucket idea, very different concurrency mechanics.

📌 Key IdeaHashMap optimizes for single-threaded performance with bucket chains/tree bins and periodic resize. ConcurrentHashMap uses fine-grained synchronization + CAS + lock-free reads to scale under concurrent access.
✨

Why It Matters

  • Interview favorite — Common mid/senior Java question: explain bucketing, collisions, resize, and thread-safety trade-offs.
  • Bug prevention — Knowing internals helps avoid race conditions, lost updates, and performance cliffs.
  • API correctness — Choosing computeIfAbsent vs get+put often decides correctness under load.
🖼

Visual

🧱 HashMap internals (single-threaded view)
hash(key) -> index = (n - 1) & hash
table[index] -> bucket

bucket forms:
  null
  Node chain (linked list)
  TreeNode (red-black tree) when collisions high

resize when size > threshold = capacity * loadFactor
🧵 ConcurrentHashMap internals (multi-threaded)
No global map lock for reads

put() path:
  CAS insert into empty bin OR
  lock only that bin while mutating

high collisions -> tree bin
resize uses cooperative transfer by multiple threads

atomic APIs: compute(), merge(), putIfAbsent()
💻

Code Variations

1️⃣ HashMap bucket index formulajava
int h = key.hashCode();
int spread = h ^ (h >>> 16);      // spread high bits
int idx = (table.length - 1) & spread;
Power-of-two capacity makes bit-mask indexing fast.
2️⃣ Wrong concurrent pattern (race)java
Map<String, Integer> m = new HashMap<>();
// Thread A and B run simultaneously:
Integer v = m.get(k);        // both read same old value
m.put(k, v + 1);             // lost update
HashMap gives no atomicity for compound read-modify-write.
3️⃣ Correct atomic update in ConcurrentHashMapjava
ConcurrentHashMap<String, LongAdder> counts = new ConcurrentHashMap<>();
counts.computeIfAbsent(key, k -> new LongAdder()).increment();
Use atomic map methods + LongAdder for high-contention counters.
4️⃣ Treeification threshold intuitionjava
// Conceptual JDK behavior
if (binCount >= 8 && table.length >= 64) {
    treeifyBin(); // linked-list bucket -> red-black tree
}
Tree bins reduce worst-case lookup under heavy collisions.
📊

HashMap vs ConcurrentHashMap internals

AspectHashMapConcurrentHashMap
Thread safety❌ Not safe for concurrent mutation✅ Safe for concurrent access/mutation
Read pathFast single-threadedMostly lock-free reads
Write pathNo synchronizationCAS + per-bin locking
Collision handlingList -> tree binList/tree bin with synchronized bin mutation
Resize behaviorSingle-thread resize/rehashConcurrent cooperative transfer
Null supportAllows null key/valueDisallows null key/value
🎤

Interview Q&A

🧾

Quick Cheat Sheet

HashMap coreArray of bins + list/tree collision structures.
CHM coreBin-level synchronization + CAS + lock-free reads.
Golden ruleNever mutate HashMap from multiple threads without external synchronization.
Counter patternConcurrentHashMap + LongAdder.
🔗

Related Topics