Back to LinkedHashMap, TreeMap & Map Comparisons
Q4Question 4 of 6

Which Maps allow null keys?

Key Concepts
  • HashMap allows exactly one null key (stored in bucket 0 with hash 0) and any number of null values
  • LinkedHashMap extends HashMap and inherits the same null handling: one null key, multiple null values
  • TreeMap does NOT allow null keys because it must compare keys using compareTo() or a Comparator, and null.compareTo() throws NullPointerException
  • TreeMap does allow null values because values are never compared
  • ConcurrentHashMap and Hashtable reject both null keys and null values
  • Workaround for TreeMap null keys: use Comparator.nullsFirst(naturalOrder()) to place null before all other keys

Definition

HashMap and LinkedHashMap allow one null key and multiple null values; TreeMap rejects null keys because compareTo cannot handle null; ConcurrentHashMap rejects both.
  • HashMap/LinkedHashMap: one null key (stored in bucket 0, hash = 0), multiple null values
  • TreeMap: null key throws NullPointerException because compareTo() or Comparator.compare() is called on the key, and null has no natural ordering
  • TreeMap values: null values are allowed (values are not compared)
  • ConcurrentHashMap/Hashtable: no null keys, no null values (ambiguity in concurrent context)
  • Workaround: new TreeMap<>(Comparator.nullsFirst(Comparator.naturalOrder())) accepts null keys

HashMap has a special "unlabelled drawer" (null key) – the bank allows exactly one such drawer. TreeMap is an alphabetically sorted filing system, and null has no alphabetical position – does it go before A? After Z? The system cannot compare it to anything, so it rejects null entirely. ConcurrentHashMap rejects null because when get() returns null, you cannot tell if the key is absent or maps to null.

Understanding the Concepts

Which Maps allow null keys?

| Map Type | Null key | Null value |
|---|---|---|
| HashMap | 1 allowed (bucket 0) | Multiple allowed |
| LinkedHashMap | 1 allowed (bucket 0) | Multiple allowed |
| TreeMap | Not allowed (NPE) | Multiple allowed |
| Hashtable | Not allowed (NPE) | Not allowed (NPE) |
| ConcurrentHashMap | Not allowed (NPE) | Not allowed (NPE) |
| EnumMap | Not allowed (NPE) | Multiple allowed |
| IdentityHashMap | 1 allowed | Multiple allowed |

JAVA
package com.bank.demo;

import java.math.BigDecimal;
import java.util.*;

public class NullKeyDemo {
    public static void main(String[] args) {
        // HashMap: null key goes to bucket 0
        Map<String, BigDecimal> hashMap = new HashMap<>();
        hashMap.put(null, new BigDecimal("1000000"));
        hashMap.put("SAV2001", null);  // null value OK
        System.out.println("HashMap null key: " + hashMap.get(null));
        System.out.println("HashMap null value: " + hashMap.get("SAV2001"));

        // LinkedHashMap: same as HashMap
        Map<String, BigDecimal> linkedMap = new LinkedHashMap<>();
        linkedMap.put(null, BigDecimal.ZERO);
        System.out.println("LinkedHashMap null key: " + linkedMap.get(null));

        // TreeMap: null key throws NPE
        Map<String, BigDecimal> treeMap = new TreeMap<>();
        treeMap.put("SAV2001", null); // null value OK
        try {
            treeMap.put(null, BigDecimal.ZERO); // NPE!
        } catch (NullPointerException e) {
            System.out.println("TreeMap null key: NullPointerException");
        }

        // ConcurrentHashMap: no null key or value
        Map<String, BigDecimal> chm = new java.util.concurrent.ConcurrentHashMap<>();
        try {
            chm.put(null, BigDecimal.ZERO);
        } catch (NullPointerException e) {
            System.out.println("CHM null key: NullPointerException");
        }
        try {
            chm.put("SAV2001", null);
        } catch (NullPointerException e) {
            System.out.println("CHM null value: NullPointerException");
        }
    }
}
TEXT
HashMap null key: 1000000
HashMap null value: null
LinkedHashMap null key: 0
TreeMap null key: NullPointerException
CHM null key: NullPointerException
CHM null value: NullPointerException

Why does HashMap store null key in bucket 0?

HashMap's internal hash() method special-cases null:

JAVA
static final int hash(Object key) {
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

When the key is null, the hash is 0. Bucket index = 0 & (capacity - 1) = 0. So the null key always goes to bucket 0.

Why does TreeMap reject null keys?

TreeMap uses compareTo() (or a Comparator's compare()) to position entries in the Red-Black tree. When you call null.compareTo(anything), Java throws NullPointerException. There is no natural ordering position for null.

The exception occurs on the second put(), not the first. The first entry has nothing to compare against and is placed at the root. The second entry must be compared with the root key, and if either is null, the comparison fails.

JAVA
// TreeMap internal put logic (simplified)
public V put(K key, V value) {
    Entry<K,V> t = root;
    if (t == null) {
        // First entry: no comparison needed
        root = new Entry<>(key, value, null);
        return null;
    }
    int cmp;
    Entry<K,V> parent;
    Comparator<? super K> cpr = comparator;
    if (cpr != null) {
        // Comparator: cpr.compare(key, t.key) – NPE if key is null!
    } else {
        // Natural ordering: ((Comparable<K>)key).compareTo(t.key) – NPE if key is null!
    }
}

How do you allow null keys in TreeMap?

Use Comparator.nullsFirst() or Comparator.nullsLast() to define where null should sort:

JAVA
package com.bank.demo;

import java.util.*;

public class TreeMapNullWorkaround {
    public static void main(String[] args) {
        // Null sorts before all other keys
        TreeMap<String, String> nullFirst = new TreeMap<>(
            Comparator.nullsFirst(Comparator.naturalOrder())
        );
        nullFirst.put(null, "Default");
        nullFirst.put("SAV2001", "Rajesh");
        nullFirst.put("CUR3001", "Priya");
        System.out.println("nullsFirst: " + nullFirst);

        // Null sorts after all other keys
        TreeMap<String, String> nullLast = new TreeMap<>(
            Comparator.nullsLast(Comparator.naturalOrder())
        );
        nullLast.put(null, "Default");
        nullLast.put("SAV2001", "Rajesh");
        System.out.println("nullsLast: " + nullLast);
    }
}
TEXT
nullsFirst: {null=Default, CUR3001=Priya, SAV2001=Rajesh}
nullsLast: {SAV2001=Rajesh, null=Default}

What is the best practice for null handling in maps?

Avoid null keys and null values. Use sentinel objects or Optional instead.

JAVA
// Instead of null key:
private static final String DEFAULT_KEY = "__DEFAULT__";
map.put(DEFAULT_KEY, defaultValue);

// Instead of null value:
map.put("SAV2001", Optional.empty()); // explicit "no value"
// Or use getOrDefault:
BigDecimal balance = map.getOrDefault("UNKNOWN", BigDecimal.ZERO);

What should a senior engineer know?

- Migration risk. Code that uses HashMap with null keys will break when migrated to ConcurrentHashMap or TreeMap. Always search for null key usage before switching map implementations.

- containsKey vs get for null. get(key) returning null is ambiguous: key absent or value is null. Use containsKey(key) to distinguish. But in concurrent maps, this check-then-act is racy, which is why ConcurrentHashMap rejects null entirely.

What are common interview follow-up questions?

  • "How does HashMap store the null key?" – Hash 0, bucket 0.
  • "Why does TreeMap reject null keys?" – compareTo throws NPE on null.
  • "Why does ConcurrentHashMap reject null values?" – get() returning null would be ambiguous.
  • "How do you allow null keys in TreeMap?" – Comparator.nullsFirst() or nullsLast().

Visual Overview

DIAGRAM
  Null key handling

  HashMap:           null → hash(null) = 0 → bucket 0
  LinkedHashMap:     null → hash(null) = 0 → bucket 0
  TreeMap:           null → compareTo(?) → NPE!
  ConcurrentHashMap: null → rejected (ambiguous)
  Hashtable:         null → rejected (legacy)

  TreeMap workaround:
  Comparator.nullsFirst(naturalOrder())
  → null sorts before all keys

Key Takeaway

HashMap and LinkedHashMap allow one null key (stored in bucket 0) and any number of null values. TreeMap rejects null keys because it must compare every key using compareTo or a Comparator, and null has no natural ordering. ConcurrentHashMap rejects both null keys and null values to avoid ambiguity in concurrent contexts.

The best practice is to avoid null keys and values entirely. Use sentinel objects, Optional, or getOrDefault() instead. If you must support null keys in a sorted map, use Comparator.nullsFirst(naturalOrder()) to define where null should sort.

In the National Bank of India system, Rajesh enforces Objects.requireNonNull(accountId) at every service entry point, ensuring that null never reaches a map key. This eliminates an entire category of bugs and makes the code safe to migrate between HashMap, TreeMap, and ConcurrentHashMap without surprises.

Remember one sentence: HashMap allows one null key in bucket 0; TreeMap rejects null because compareTo cannot order it; avoid null keys entirely for clean, portable code.

---

Share
Use ← → arrow keys to navigate