WeakHashMap, IdentityHashMap and EnumMap
WeakHashMap, IdentityHashMap, EnumMap and case-insensitive Map choices, including GC behaviour, reference equality and enum-key performance.
On this page
🗺️ What are WeakHashMap, IdentityHashMap and EnumMap?
WeakHashMap, IdentityHashMap and EnumMap are special-purpose Map implementations that change one of the usual HashMap assumptions. WeakHashMap lets keys disappear when the rest of the application no longer strongly references them, IdentityHashMap compares keys with ==, and EnumMap stores enum keys in a compact array. If HashMap is the normal toolbox drawer, these are the oddly shaped tools you use when the requirement is oddly shaped too.
For this lesson, we'll focus on how WeakHashMap uses WeakReference and ReferenceQueue, why it is not a general cache, when reference equality makes IdentityHashMap the right answer, why EnumMap is the best default for enum keys, and how to build maps with case-insensitive String keys. Interviewers ask these because they look like trivia until you hit the edge cases. Then the answer tells me whether you understand object lifetime, equality and the real contract of Map.
Below the line (out of scope)
- Hash table buckets, resizing and tree bins: How HashMap works
- LRU eviction with access order: LinkedHashMap and building an LRU cache
- Sorted maps and comparator consistency: TreeMap and red-black trees
- Full GC reference types and reachability: covered in the JVM and memory lessons later
The 30-second answer
Use WeakHashMap when the key's object lifetime should control whether metadata stays in the map: keys are wrapped in weak references, the GC clears them, and the map removes stale entries by draining a ReferenceQueue during normal operations. Do not treat it as a cache: string literals, interned strings and boxed small integers may never become unreachable, and a value that points back to its key keeps the key alive. Use IdentityHashMap when == is the correct equality rule, such as graph traversal, serialization or proxy bookkeeping; it intentionally violates the normal Map contract and stores alternating keys and values in a flat Object[] with linear probing. Use EnumMap for enum keys: it stores values in an array indexed by ordinal(), iterates in declaration order, rejects null keys and is usually the fastest map for enum keys. For case-insensitive strings, choose either a TreeMap<>(String.CASE_INSENSITIVE_ORDER) or normalize keys with toLowerCase(Locale.ROOT) before a HashMap lookup.
What you’ll learn
- Explain how
WeakHashMapstores keys through weak references and removes stale entries through aReferenceQueue - Avoid the
WeakHashMaptraps: interned keys, boxed small integers, and values that strongly reference their keys - Use
IdentityHashMaponly when reference equality is the requirement, not as a fasterHashMap - Describe
EnumMapas an array indexed by enumordinal()and know why it preserves declaration order - Choose a case-insensitive map strategy:
TreeMap(String.CASE_INSENSITIVE_ORDER), normalizedHashMap, or a library map - Compare the special map implementations by equality rule, ordering, null handling and interview use case
Interview radar
Asked sometimes: Which map would you use for keys compared by reference equality? Which map lets entries disappear when keys are no longer referenced?
Senior rounds probe: Why WeakHashMap can still leak, why IdentityHashMap breaks the usual equality contract, and why EnumMap is an array, not a hash table.
Production angle: A metadata map that grows with object lifetime is useful. A weak map pretending to be a cache usually becomes a confusing memory bug.
01Picture it first: luggage tags, mirror IDs and mail slots
Think of these maps as three different reception desks. WeakHashMap keeps a luggage tag that falls off when the suitcase is gone. IdentityHashMap checks the physical badge, not the name printed on it. EnumMap has one fixed mail slot per enum constant, so MONDAY always goes to slot 0 if it was declared first.
Let's get the WeakHashMap picture right first. The key is weak; the value is not.
A WeakHashMap entry holds its key through a WeakReference registered with a ReferenceQueue; when the application drops the last strong reference, GC clears the weak key and the next map operation removes the stale entry while the value was always strongly held
Notice the shape. The map does not remove the entry at the exact instant the key becomes unreachable. The GC clears the weak reference and enqueues it, then a later map operation drains the queue and unlinks the stale entry.
02WeakHashMap under the hood
WeakHashMap<K,V> is a hash table whose entry extends WeakReference<Object>. The referent is the key. The entry also stores the value, the cached hash and the next pointer in the bucket chain.
Try it yourself first
Before reading the answer, predict which entries are likely to disappear after GC and which ones are likely to stay.
import java.util.Map;
import java.util.WeakHashMap;
public class WeakKeysDemo {
public static void main(String[] args) throws Exception {
Map<Object, String> metadata = new WeakHashMap<>();
Object request = new Object();
String literal = "tenant-a";
Integer small = Integer.valueOf(42);
metadata.put(request, "request metadata");
metadata.put(literal, "literal metadata");
metadata.put(small, "small integer metadata");
request = null;
System.gc();
Thread.sleep(100);
metadata.size(); // touches the map and gives it a chance to expunge stale entries
System.out.println(metadata);
}
}
The request entry is the one that can disappear, because after request = null nothing else strongly refers to that object. The string literal is held by the class's constant data for as long as the class is loaded. The small Integer comes from IntegerCache for values from -128 to 127, so the cached object is strongly reachable through the JDK cache.
Notice three things:
WeakHashMapcalls an internalexpungeStaleEntrieswhile serving normal operations such asget,put,remove,sizeand table access. Cleanup is lazy from your point of view.- The value is a strong reference. If the value points back to the key, directly or through a graph, the key is still reachable and the entry does not disappear.
- GC timing is not a business rule. You cannot write correctness logic that depends on an entry vanishing right after
System.gc().
A real stale entry goes through four steps. The application drops its last strong reference to the key. The GC discovers that only weak references point at the key, clears the weak reference, and enqueues the entry's reference on the map's queue. A later map operation drains that queue, finds the original entry and removes it from the bucket.
That makes WeakHashMap useful for metadata keyed by object lifetime. For example, a framework might attach analysis data to AST nodes, parsed templates or connection wrapper objects without wanting a side table to keep those objects alive. For class-specific metadata, ClassValue<T> is often the cleaner API because the JVM owns the class-lifetime association.
The first weak-map bug I saw in a code review was a "temporary" cache keyed by Class<?>. The values included helper objects that held the same Class<?> for logging and reflection. That one back-reference turned the weak-key map into a strong-key map, and old plugin class loaders stayed alive after reloads. The fix was either ClassValue or values that did not retain the key.
03IdentityHashMap: reference equality on purpose
Premium lesson
Keep reading with the Java Pass
The rest of this lesson is part of Java One-Stop Interview Prep: in-depth explanations, diagrams, model answers and how to say them in the interview.
- EnumMap: one slot per enum constant
- Case-insensitive String keys
- Advanced: choosing among the special maps
- How to say it in the interview
From ₹199 · one-time payment · No auto-renewal