Can HashMap Store a null Key
In java.util.HashMap, the hash function explicitly guards against null using a ternary check:
🟢 Junior Level
Yes, HashMap permits storing a null key, but only as a single unique entry.
Key Rules & Behaviors:
- Uniqueness: Just like any other key in
HashMap, duplicates are not allowed. Callingmap.put(null, "new value")a second time simply overwrites the previous value associated withnull. - Fixed Bucket Zero (
table[0]): The hash code for anullkey is hardcoded to0. Consequently,(n - 1) & 0always evaluates to0, placing the entry strictly into bucket0(table[0]). - Null-Safe Operations: Storing or retrieving a
nullkey does not throw aNullPointerExceptionbecauseHashMapnever invokes.hashCode()or.equals()onnull.
import java.util.HashMap;
import java.util.Map;
public class NullKeyDemo {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put(null, "Default Value");
System.out.println(map.get(null)); // "Default Value"
// Overwriting the existing null key
map.put(null, "Updated Value");
System.out.println(map.get(null)); // "Updated Value"
System.out.println(map.size()); // 1 (size does not grow)
}
}
Analogy: A
nullkey is like a dedicated “Uncategorized” folder on the very first shelf (drawer 0) of a filing cabinet. You can only have one such folder; placing a new document inside replaces the previous one.
🟡 Middle Level
The Hash Function for null
In java.util.HashMap, the hash function explicitly guards against null using a ternary check:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
The bucket index calculation uses the power-of-two bitwise mask:
\[\text{index} = (n - 1) \ \& \ \text{hash} = (n - 1) \ \& \ 0 = 0\]Therefore, a null key is deterministic and invariably mapped to table[0].
Node Lookup and Equality Verification
Bucket table[0] is not isolated; non-null keys with hash codes that evaluate to index 0 can share bucket 0. When traversing the bucket during get(null) or put(null, value), HashMap evaluates:
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
When searching for key = null:
hashis0, matchingp.hash == 0.- The reference check
(k = p.key) == keyevaluatesp.key == null. - Because
key != nullis false, short-circuit evaluation preventskey.equals(k)from ever being called. - No
NullPointerExceptioncan occur.
Map Implementation Comparison
| Map Implementation | null Key Allowed |
null Values Allowed |
Behavior on put(null, ...) |
|---|---|---|---|
HashMap |
✅ Yes (Single key) | ✅ Yes | Stored at table[0] |
LinkedHashMap |
✅ Yes (Single key) | ✅ Yes | Stored at table[0] + linked in iteration list |
ConcurrentHashMap |
❌ No | ❌ No | Throws NullPointerException immediately |
Hashtable |
❌ No | ❌ No | Throws NullPointerException (key.hashCode()) |
TreeMap |
⚠️ Conditionally | ✅ Yes | Throws NPE by default; allowed if custom comparator handles null |
Map.of(...) (Java 9+) |
❌ No | ❌ No | Throws NullPointerException (strict contract) |
🔴 Senior Level
Evolution of null Handling: Java 7 vs Java 8+
In Java 7, HashMap maintained dedicated internal helper methods for null keys:
putForNullKey(V value)getForNullKey()
Java 7 bypassed the general hashing loop for null to avoid conditional checks inside the core traversal routines.
In Java 8+, the implementation was unified:
hash(key)returns0whenkey == null.- The standard execution path (
putVal,getNode,removeNode) processesnullidentically to any other key with hash0. - The specialized
putForNullKeyandgetForNullKeymethods were removed from OpenJDK.
Myth: Bucket table[0] Is Exclusively Reserved for null
A common misconception is that table[0] belongs solely to the null key. In reality:
- Any object where
hash(key) == 0lands in bucket0(e.g.,""or custom objects returning 0). - Any key whose low-order bits match
0modulo table capacity lands intable[0](e.g., hash code16,32,48when capacity is 16). - If 8 or more colliding non-null keys enter
table[0]alongsidenull, and the map’s capacity is $\ge 64$, bucket0will treeify into a Red-Black Tree (TreeNode)! - Inside a treeified bucket 0, the
nullnode becomes aTreeNode. Becausenulldoes not implementComparable, tree balancing resolves ordering via the fallback methodtieBreakOrder(a, b)usingSystem.identityHashCode(a).
Architectural Risks & Production Pitfalls
- Refactoring to Concurrent Collections: If application logic permits
nullkeys inHashMap, later refactoring toConcurrentHashMapfor thread safety causes silent runtimeNullPointerExceptionfailures. - Incompatibility with Modern Java APIs: Immutable map factories (
Map.of(),Map.ofEntries()) and stream collectors (Collectors.toUnmodifiableMap()) strictly prohibitnullkeys and values viaObjects.requireNonNull(). - Domain-Driven Design (DDD) Violation: In business domains, a Map key models a domain identity (e.g.,
CustomerId,TransactionUuid). A missing identity (null) represents an invalid state or uninitialized entity, which should never be used as a lookup index.
Recommended Alternative: Null Object / Sentinel Key Pattern
// Anti-pattern:
Map<String, Handler> handlers = new HashMap<>();
handlers.put(null, defaultHandler);
// Clean DDD Pattern:
public enum RouteKey {
STANDARD,
PREMIUM,
ANONYMOUS_FALLBACK
}
Map<RouteKey, Handler> safeHandlers = new ConcurrentHashMap<>();
safeHandlers.put(RouteKey.ANONYMOUS_FALLBACK, defaultHandler);
4 Tricky Questions
1. Can a TreeMap store a null key?
Answer:
By default, no; but with a custom Comparator, yes.
By default, new TreeMap<>() uses the natural ordering of keys (Comparable). During the first or subsequent insertions, k.compareTo(...) is called, throwing an immediate NullPointerException.
However, if the TreeMap is instantiated with a null-friendly comparator, it works seamlessly:
Map<String, String> treeMap = new TreeMap<>(Comparator.nullsFirst(Comparator.naturalOrder()));
treeMap.put(null, "Safe Value");
System.out.println(treeMap.get(null)); // "Safe Value"
The comparator intercepts null prior to invoking methods on the object, positioning null at the extreme left (or right with nullsLast) of the Red-Black Tree.
2. What happens if you wrap a HashMap containing a null key with Collections.unmodifiableMap()?
Answer:
The code executes normally without exceptions.
Collections.unmodifiableMap is a lightweight wrapper decorator. It forwards read calls directly to the backing map and blocks mutating operations (put, remove, clear). If the underlying HashMap contains a null key, unmodifiableMap.get(null) and unmodifiableMap.containsKey(null) work as expected.
3. Can bucket table[0] containing a null key treeify into a TreeNode?
Answer:
Yes.
If 8 or more entries with hash codes resolving to index 0 are added to table[0] alongside null, and the total capacity of the map is at least 64 (MIN_TREEIFY_CAPACITY), table[0] treeifies.
The entry with the null key is converted into a TreeNode. When traversing or inserting into the tree, because null does not implement Comparable, HashMap falls back to tieBreakOrder(a, b) using System.identityHashCode().
4. Why does Map.of(null, "value") throw an NPE while HashMap permits null?
Answer:
Modern Java APIs (Java 9+) follow stricter defensive programming standards. Brian Goetz and the OpenJDK team considered the permission of null in legacy collections (Java 1.2) to be an evolutionary mistake (“billion-dollar mistake”) that fosters subtle bugs, ambiguate map queries (get(k) == null meaning key absent vs mapped to null), and hinders internal layout optimizations (e.g., Valhalla value objects). Hence, all modern factories (List.of, Set.of, Map.of) enforce Objects.requireNonNull.
🎯 Interview Cheat Sheet
- Permission:
HashMapallows exactly onenullkey. Inserting again overwrites the previous value. - Bucket Location: Always mapped to
table[0]becausehash(null) == 0and(n - 1) & 0 == 0. - Comparison: Evaluated via reference equality
p.key == null..equals()is never invoked onnull, preventingNullPointerException. - Bucket Zero Is Shared:
table[0]is not reserved fornull; non-null keys with colliding hash codes can reside in and even treeifytable[0]. - Other Collections:
ConcurrentHashMap,Hashtable: Strictly prohibitnullkeys (throws NPE).TreeMap: Throws NPE by default; permitted only withComparator.nullsFirst(...).Map.of(...): Strictly prohibitsnullkeys (throws NPE).
- Production Best Practice: Avoid
nullkeys. Use the Null Object pattern or sentinelenumconstants to ensure seamless migration to concurrent collections and modern immutable APIs.