The 30 second answer
I pick a Map by the contract I need. HashMap for plain lookups, LinkedHashMap when insertion or access order matters, which also gives a small LRU cache. TreeMap for sorted keys and range queries in guaranteed log time. EnumMap for enum keys, backed by an array. ConcurrentHashMap or ConcurrentSkipListMap when threads share it. WeakHashMap and IdentityHashMap are special cases with traps worth naming.
Public candidate reviews of Zalando interviews mention "maps, its implementations and working" as a topic. HashMap internals are covered in the ConcurrentHashMap answer earlier in this chapter; this answer is about choosing between the implementations, which is where interviewers usually push next.
Start from the contract, not the class. Ask what the caller needs: plain lookup, a predictable iteration order, sorted keys and range queries, enum keys, safe sharing between threads, or special key semantics. Each standard implementation exists for one of those.
| Implementation | What it guarantees | Typical use |
|---|---|---|
HashMap | Expected constant-time get and put with a good hashCode; no order guarantee | Default lookup table |
LinkedHashMap | Insertion order, or access order when constructed with accessOrder = true | Stable output order, small LRU caches |
TreeMap | Red-black tree, sorted by natural order or a Comparator, guaranteed log(n) for get, put, remove | Range queries: floorKey, ceilingKey, subMap |
EnumMap | Keys from one enum type, stored internally as an array, iterated in declaration order | Per-status or per-type counters |
ConcurrentHashMap | Thread-safe, non-blocking reads, atomic compute and merge | Shared caches and counters |
ConcurrentSkipListMap | Sorted and concurrent, expected average log(n), weakly consistent iterators | Shared sorted index, time-ordered buffers |
WeakHashMap | Entries disappear once the key is no longer strongly reachable | Metadata attached to objects you do not own |
IdentityHashMap | Compares keys with == instead of equals | Graph traversal, serialization bookkeeping |
LinkedHashMap as an LRU cache. The Javadoc says an access-ordered LinkedHashMap is "well-suited to building LRU caches", and removeEldestEntry lets you evict on insert:
public class LruCache<K, V> extends LinkedHashMap<K, V> {
private final int maxEntries;
public LruCache(int maxEntries) {
super(16, 0.75f, true); // true = access order, so get() moves an entry to the end
this.maxEntries = maxEntries;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxEntries; // evict the least recently accessed entry
}
}Verified with OpenJDK 21.0.13: with a capacity of two, putting dress and shoes, reading dress, then putting jacket leaves [dress, jacket]. Two caveats to say out loud. In access-order mode even get is a structural modification, so the class is not safe to share without external synchronization. For a production cache that many request threads share, a purpose-built cache library or a ConcurrentHashMap with a bounded eviction policy is the better answer.
TreeMap versus ConcurrentSkipListMap. Both are NavigableMaps with sorted keys and range views. TreeMap is single-threaded with a guaranteed log(n) bound. ConcurrentSkipListMap lets threads insert and read at the same time with an expected, not guaranteed, log(n) cost, and its iterators are weakly consistent. Use it when the sorted structure is shared, for example a time-ordered buffer of index updates.
The two traps. WeakHashMap holds keys weakly but values strongly, and the Javadoc warns that a value that refers back to its own key, directly or indirectly, prevents the key from ever being collected, which turns the "self-cleaning" map into a leak. IdentityHashMap "intentionally violates Map's general contract" by comparing with ==; two equal Strings built separately are two different keys.
How to close the answer. "In a search service, most maps are HashMaps inside one request. Shared state is a ConcurrentHashMap with atomic compute or merge, sorted shared state is a skip list, and anything that must evict is a real bounded cache, not an unbounded map."
Alternate interview scenarios
"You need the top N most recent queries, kept sorted, and several threads write to it. Which structure?"
I want ordering and concurrency together, so I reach for ConcurrentSkipListMap keyed by timestamp plus a tie-breaker, because two queries can share a millisecond. Writers call put, and a trimming step calls pollFirstEntry while the size is above N. Readers iterate descendingMap() for newest first, accepting that the view is weakly consistent. If only one thread writes, a TreeMap behind a lock is simpler and just as good.
"Why might an EnumMap beat a HashMap keyed by an enum?"
The Javadoc says enum maps are represented internally as arrays, which it calls extremely compact and efficient, so a lookup is an index by ordinal, with no hashing and no collisions. Iteration follows the enum's declaration order, which keeps reports and logs stable. Null keys are not allowed, which is usually what you want for an enum key anyway.
<!-- include: java-spring/java-8-to-21-interview-questions#what-are-functional-interfaces-and-lambdas --> <!-- why: Search code uses lambdas heavily in collectors, callbacks and comparator chains. -->
<!-- include: java-spring/java-8-to-21-interview-questions#how-does-the-stream-api-work-internally --> <!-- why: Scoring, filtering and grouping pipelines are easy to write as streams, and easy to get wrong on large result sets. -->
<!-- include: java-spring/java-8-to-21-interview-questions#what-is-the-difference-between-future-and-completablefuture --> <!-- why: A search API layer fans out to Elasticsearch, ranking and features; composition and errors live in CompletableFuture. -->
<!-- include: java-spring/java-8-to-21-interview-questions#how-does-optional-help-avoid-null-checks --> <!-- why: Missing fields on catalog documents are normal; Optional is how many codebases make that absence explicit. -->
<!-- include: java-spring/java-8-to-21-interview-questions#what-is-the-difference-between-default-and-static-methods --> <!-- why: Shared search contracts often grow default methods, and you need to know what that does to implementors. -->
<!-- include: java-spring/java-8-to-21-interview-questions#how-would-you-migrate-an-application-from-java-8-to-java-21 --> <!-- why: A long-lived search service is usually mid-migration, where virtual threads, records and removed APIs come up. -->
<!-- include: api-performance/api-latency-optimization#how-do-you-make-sure-an-application-meets-its-performance --> <!-- why: Whether a search service meets its SLOs is a measurement problem before it is a tuning problem. -->
<!-- include: java-spring/java-8-to-21-interview-questions#how-do-you-troubleshoot-high-cpu-in-a-java-service --> <!-- why: A hot query or analysis path shows up as CPU first; you need a method for finding the frames that burn it. -->
Sources (6)
- Java SE 21 API: LinkedHashMapdocs.oracle.com
- Java SE 21 API: TreeMapdocs.oracle.com
- Java SE 21 API: EnumMapdocs.oracle.com
- Java SE 21 API: ConcurrentSkipListMapdocs.oracle.com
- Java SE 21 API: WeakHashMapdocs.oracle.com
- Java SE 21 API: IdentityHashMapdocs.oracle.com