■■ HashMap – How It Really Works InternallySenior Java Engineer Interview Deep-Dive GuideHashing · Buckets · Collisions · Resize · TreeBin · ConcurrentHashMap · Java 8–21
| Prepared for | Ashok Koritala – Senior Java Full Stack Developer (15+ yrs) |
| Java Version | Java 7 → Java 21 (all critical changes covered) |
| Questions | 35 expert-level Q&A; across 7 topic clusters |
| Scope | HashMap internals · hashCode/equals contract · collision handling · resize/rehash · TreeBin (Java 8) · LinkedHashMap · WeakHashMap · ConcurrentHashMap · real-world gotchas |
| n = 16 → n-1 = 15 = 0000 1111 hash = ???? ???? (any 32-bit int)index = hash & 15 = last 4 bits only → always 0..15e.g. hash = 0b10110101_11001010_00101101_10110111 mask = 0000 1111 index = 0000 0111 = 7 |
| ■ Interview Tip: If an interviewer asks 'why not use hash % n?', answer: (n-1) & hash is identical when n is power-of-2but is a single CPU instruction vs an expensive integer division. |
| HashMap.table (Node[] – capacity = 16 default) ■■■■■■■■ 0 ■ ■■■ null■■■■■■■■ 1 ■ ■■■ Node{hash,key='A',val=1,next} ■■■ Node{hash,key='Q',val=9,next=null}■■■■■■■ (linked list – 2 colliding keys)■ 2 ■ ■■■ null■■■■■■■■ 3 ■ ■■■ TreeNode (root of red-black tree – 8+ collisions)■ ■ / \■ ■ TreeNode TreeNode■ ■ / \■ ■ TreeNode TreeNode■■■■■■■■ 4 ■ ■■■ Node{hash,key='B',val=2,next=null} (single entry)■■■■■■■■ ... ■■■■■■■■■ 15 ■ ■■■ null■■■■■■■Fields: size=5, threshold=12, loadFactor=0.75, modCount=5 |
| ■ Interview Tip: Being able to draw this diagram in an interview immediately shows deep understanding. Label the Nodefields (hash, key, value, next) and explain when TreeNode kicks in. |
| rawHash = 1111 1111 0000 1111 1010 1010 0011 0011 h>>>16 = 0000 0000 0000 0000 1111 1111 0000 1111XOR = 1111 1111 0000 1111 0101 0101 0011 1100 ← spread hashn-1 = 0000 1111 (n=16)index = 0000 1100 = 12 |
| ■ Key Insight: The h ^ (h >>> 16) spread is critical. Without it, keys whose hashCodes differ only in high bits would allland in the same bucket when the table is small. |
| Map Type | Null Key | Null Value | Thread-Safe |
| HashMap | 1 allowed (bucket 0) | Multiple allowed | No |
| LinkedHashMap | 1 allowed | Multiple allowed | No |
| TreeMap | No (NullPointerException) | Multiple allowed | No |
| Hashtable | No | No | Yes (legacy) |
| ConcurrentHashMap | No | No | Yes |
| WeakHashMap | 1 allowed | Multiple allowed | No |
| Before resize (n=16): After resize (n=32): bucket[5] → A → B → C bucket[5] → A → C (bit4=0) bucket[21] → B (bit4=1) |
| ■ Key Insight: Java 8 avoids recomputing hashCode during resize by using the cached hash in each Node and a singlebit-check. This makes resize significantly faster than Java 7. |
| Load Factor | Memory Use | Collision Rate | Lookup Speed | Resize Frequency | Best For |
| 0.25 | Very high (many empty buckets) | Very low | O(1)near-perfect | Very frequent | Ultra low-latency lookups |
| 0.5 | High | Low | Very good | Frequent | Low-latency apps |
| 0.75 | Balanced (default) | Moderate | Good | Normal | General purpose |
| 1.0 | Low (fewer resizes) | High | Degrades | Infrequent | Memory-constrained |
| >1.0 | Minimal | Very high | Poor (O(n) risk) | Rarely | Avoid in production |
| ■ Interview Tip: For read-heavy, performance-critical maps, consider load factor 0.5 or even 0.25 with a pre-computedinitial capacity. The extra memory cost is often worth it. |
| Operation | Best Case | Average | Worst (Java 7) | Worst (Java 8+) |
| put() | O(1) | O(1) | O(n) – all keys same bucket | O(log n) – TreeBin |
| get() | O(1) | O(1) | O(n) | O(log n) |
| remove() | O(1) | O(1) | O(n) | O(log n) |
| resize() | – | O(n) | O(n) | O(n) |
| containsKey() | O(1) | O(1) | O(n) | O(log n) |
| keySet() iteration | O(n+buckets ) | O(n+buckets ) | O(n+buckets) | O(n+buckets) |
| ■ Key Insight: O(n+buckets) for iteration is why iterating a HashMap with capacity=1,000,000 but size=10 visits1,000,010 slots. Use HashMap(16) when you know the map will stay small. |
| Feature | HashMap | ConcurrentHashMap | Hashtable | Collections.synchronizedMap |
| Thread-safe | No | Yes | Yes | Yes |
| Null key | 1 allowed | No | No | Depends on delegate |
| Null value | Yes | No | No | Depends on delegate |
| Locking | None | CAS + bucket lock | Full lock | Full lock on wrapper |
| Iteration | Fail-fast | Weakly consistent | Fail-fast | Fail-fast (manual sync) |
| Java version | 1.2 | 1.5 | 1.0 (legacy) | 1.2 |
| Performance | Best ST | Best MT | Poor | Poor |
| size() accuracy | Exact | Approximate | Exact | Exact (locked) |
| ■■ Watch Out: Never use Hashtable or Collections.synchronizedMap() in new code. ConcurrentHashMap is the correct choice for thread-safe maps. |
| Default capacity / load factor | 16 / 0.75 → threshold = 12 |
| Bucket index formula | (n - 1) & hash(key) [where n = power of 2] |
| hash() function | h ^ (h >>> 16) – XOR high bits into low bits |
| Collision handling (Java 8) | Linked list (0–7 nodes) → Red-Black TreeBin (8+) |
| TREEIFY_THRESHOLD | 8 nodes in bucket AND table.length >= 64 |
| UNTREEIFY_THRESHOLD | 6 nodes (hysteresis – prevents thrashing) |
| MIN_TREEIFY_CAPACITY | 64 – below this, resize instead of treeify |
| Resize trigger | size > capacity * loadFactor → double capacity (O(n)) |
| Resize bucket split | Bit-check: entry stays OR moves to index + oldCapacity |
| Node fields | hash (cached), key, value, next |
| Null key handling | hash(null) = 0 → always in bucket[0] |
| pre-size formula | new HashMap<>((int)(expected / 0.75) + 1) |
| put() steps | lazy init → index → empty/tree/list → value update → resize check |
| get() complexity | O(1) avg, O(log n) worst (TreeBin), O(n) worst (Java 7) |
| ConcurrentHashMap locking | CAS for empty buckets; synchronized on bucket head (non-empty) |
| CHM size() | Approximate (LongAdder-style striped counter) |
| CHM null keys/values | NOT allowed (NullPointerException) |
| LinkedHashMap | Insertion or access order; uses doubly-linked list through entries |
| WeakHashMap | Weak keys – GC'd when no strong refs; uses ReferenceQueue |
| EnumMap | Array-backed, ordinal index; fastest for enum keys |
| Java 8 change | Tail insertion (fixes Java 7 resize infinite loop bug) |
| Fail-fast iterators | modCount check on every next(); ConcurrentModificationException |
No comments:
Post a Comment