skip to content

Map Implementations

The key-value implementations and their internals: hash tables, linked entries, red-black trees, and the specialty maps, plus the compute and merge methods. HashMap internals are the single most-asked collections topic.

part ofJavaoverview, primer and where to startread it →
on this pageshow

explore

questions

27

How does a Java HashMap store key-value pairs internally, and how does it find a value by key?

level: juniorimportance: must knowfreq 90%

answer

  1. Array of buckets, each bucket = chain of Node<K,V>
  2. put = hash to index, store; get = same index, equals to match
  3. Collision = two keys, same bucket -> chained
  4. Amortized O(1); needs correct hashCode + equals
  5. Keys should be immutable

basics

~20 s

A HashMap keeps an array of buckets. It turns each key into a number (a hash), uses that number to pick a bucket, and stores the key and value there. To get a value it hashes the key again, jumps to the same bucket, and compares keys with equals to find the match.

solid answer

~40 s

A HashMap is backed by an array (the table) of buckets. On put(k,v) it computes the key's hashCode, spreads it, and maps it to a bucket index. The key-value pair is stored as a Node in that bucket. On get(k) it repeats the index calculation, walks the bucket, and uses equals() to find the entry whose key matches, returning its value. When two different keys land in the same bucket (a collision), they are chained together in that bucket, so get still works by scanning the short chain. Because most lookups touch only one bucket, get and put are amortized O(1). This is why a key's class must implement hashCode() and equals() consistently: a broken hashCode scatters equal keys, and a broken equals breaks matching.

go deeper

for a junior

Can describe buckets, hashing a key to an index, and using equals to find the value; knows custom keys need hashCode + equals.

for a middle

Explains collisions and chaining, amortized O(1) vs worst-case, and why mutable keys break lookups.

for a senior

Connects spreading/index math, the Node structure with cached hash, and reasons about when O(1) degrades.

for a principal

Discusses key-design implications across a codebase (immutability, equals/hashCode contracts, value-based key types) and failure modes at scale.

## What a HashMap is A **HashMap** stores **key-value pairs** (called *entries* or *mappings*) and lets you look up a value by its key very fast. It implements the `Map` interface. A *key* is the thing you search by (e.g. a username); a *value* is the data attached to it (e.g. a user object). ## The core data structure: a table of buckets Internally a HashMap holds an array called the **table**. Each slot of that array is a **bucket**. A bucket can hold zero, one, or several entries. Each stored entry is a small object Java calls a `Node<K,V>` holding four things: the key, the value, the key's cached hash, and a `next` pointer to the following entry in the same bucket (forming a linked list). ## What a hash code is Every Java object has a `hashCode()` method returning an `int`. A **hash code** is just a number derived from the object's contents. The contract: if two objects are `equals()`, they MUST return the same `hashCode()`. The reverse is not required — two unequal objects may share a hash code (that is allowed and is called a *collision*). ## How put works (storing) 1. Compute `key.hashCode()`. 2. *Spread* it (HashMap mixes the high bits down — explained in the spreading question). 3. Map the spread hash to a bucket **index** in the table. 4. Go to that bucket. If empty, place the new Node there. If it already has entries, walk them: if a key already `equals()` the new key, overwrite its value (a key is unique); otherwise append the new Node to the chain. ## How get works (retrieving) 1. Compute and spread the key's hash exactly as in put. 2. Map to the same bucket index. 3. Walk that bucket's entries; for each, first compare cached hashes (cheap), then compare keys with `equals()`. Return the value of the entry whose key matches, or `null` if none matches. Because put and get use the *same* index math, a get always lands in the bucket where put placed the entry. ## Why it is fast If entries are spread evenly, each bucket holds roughly one entry, so a lookup does one index calculation plus one or two comparisons — constant time, written **O(1)** (cost does not grow with map size). The word **amortized** means "averaged over many operations": individual operations are O(1) on average even though an occasional resize is more expensive. ## Why hashCode and equals matter - A correct `hashCode()` spreads keys across buckets. A bad one (e.g. always returning `0`) dumps every key into one bucket, degrading lookups toward O(n) (a linear scan). - A correct `equals()` is how the map confirms a real match inside a bucket. If `equals()` is wrong, get may miss an entry that is actually present. This is why you must override BOTH together whenever you use a custom class as a key, and the key should be **immutable** (its hash must not change while it sits in the map, or the map can never find it again).

  • What happens if you use a mutable object as a key and then change a field that affects its hashCode?
    The map indexed it under the old hash. After mutation its new hash maps to a different bucket, so get/contains usually fail to find it even though it is still stored. Use immutable keys.
  • Why must hashCode() and equals() be overridden together?
    The contract requires equal objects to have equal hash codes. If you override only equals(), two equal keys can land in different buckets and the map treats them as distinct; if you override only hashCode(), the map can't confirm a match. Both are needed for correct lookups.

saying these in an interview costs you the question

  • Saying HashMap stores entries in sorted or insertion order
  • Claiming equal hash codes mean equal objects (collisions are normal)
  • Forgetting that equals() is still needed even with a good hashCode
  • Thinking get is always O(1) regardless of hashCode quality

context

open as a page

What is LinkedHashMap and how does its iteration order differ from HashMap?

level: juniorimportance: must knowfreq 70%

basics

~10 s

LinkedHashMap is a HashMap that also remembers the order you put entries in, so iterating returns them in insertion order. A plain HashMap gives no order guarantee and can appear random.

open as a page

What is Map.Entry and how do you use it to iterate over a Map's key-value pairs?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Map.Entry represents one key-value pair in a Map. You get all pairs with map.entrySet() and loop over them, calling getKey() and getValue() on each entry. This is the cheapest way to read both key and value together.

open as a page

How do getOrDefault and putIfAbsent simplify common Map access patterns, and how do they differ?

level: juniorimportance: must knowfreq 68%

basics

~20 s

getOrDefault(key, fallback) returns the value if the key exists, otherwise the fallback — without changing the map. putIfAbsent(key, value) only stores the value if the key is missing (or maps to null) and returns the previous value, leaving an existing value untouched.

open as a page

What is Hashtable, how does it differ from HashMap, and why is it considered a legacy class?

level: juniorimportance: must knowfreq 78%

basics

~20 s

Hashtable is an old key-value map that is thread-safe because every method is synchronized, and it refuses null keys and null values. HashMap is newer, faster, allows one null key and null values, but is not thread-safe.

open as a page

What is a TreeMap in Java and how does it differ from HashMap and LinkedHashMap?

level: juniorimportance: must knowfreq 75%

basics

~20 s

A TreeMap is a Map that keeps its keys sorted. HashMap has no order and is faster for plain get/put; LinkedHashMap keeps insertion order. TreeMap sorts keys (natural order or a Comparator) and is a bit slower.

open as a page

Explain capacity, load factor, and resizing in HashMap. What is the default load factor and why?

level: middleimportance: must knowfreq 80%

basics

~20 s

Capacity is the number of buckets (default 16). Load factor (default 0.75) is how full the map gets before it grows. When entries exceed capacity x load factor (the threshold), the map doubles its capacity and re-spreads entries so buckets stay short and lookups stay fast.

open as a page

How does HashMap handle null keys/values, what is its iteration order, and why is it not thread-safe?

level: middleimportance: must knowfreq 70%

basics

~20 s

HashMap allows one null key (stored in bucket 0) and any number of null values. Its iteration order is undefined and can change after a resize, so never rely on it. It is not thread-safe: concurrent modification can corrupt it or loop forever, so use ConcurrentHashMap for shared access.

open as a page

What does computeIfAbsent do, and why is it the idiomatic way to build multimaps (Map of List)?

level: middleimportance: must knowfreq 75%

basics

~20 s

computeIfAbsent(key, fn) returns the existing value for key, or if it is missing, runs fn to create a value, stores it, and returns it. It is perfect for grouping: it makes the empty list once, then you add to it.

open as a page

What navigation methods does NavigableMap add, and what does each of floorKey, ceilingKey, lowerEntry, and higherEntry return?

level: middleimportance: must knowfreq 68%

basics

~20 s

NavigableMap adds nearest-key lookups. For a key k: floorKey returns the largest key ≤ k, ceilingKey the smallest key ≥ k, lowerKey the largest key strictly < k, higherKey the smallest key strictly > k. The *Entry versions return the whole entry, or null if none exists.

open as a page

How do you build a bounded LRU cache using LinkedHashMap and removeEldestEntry?

level: seniorimportance: must knowfreq 75%

basics

~10 s

Create a LinkedHashMap in access-order mode and override removeEldestEntry to return true once the map grows past your size limit. After each insert, the map then automatically drops the least-recently-used entry.

open as a page

What is access-order mode in LinkedHashMap and how do you enable it?

level: middleimportance: should knowfreq 55%

basics

~20 s

Access-order mode makes LinkedHashMap move an entry to the end of its list every time you read or update it, so the least-recently-used entries stay at the front. You turn it on with the three-argument constructor passing true.

open as a page

How do compute and computeIfPresent differ from computeIfAbsent, and when would you use each?

level: middleimportance: should knowfreq 58%

basics

~20 s

computeIfAbsent runs only when the key is missing. computeIfPresent runs only when the key already has a value. compute always runs and receives the current value (or null). In all three, returning null removes (or skips creating) the entry.

open as a page

What is Map.merge and how does it express fold-style updates like frequency counting?

level: middleimportance: should knowfreq 62%

basics

~20 s

merge(key, value, fn) stores value if the key is absent; if the key already has a value, it calls fn(oldValue, value) and stores the result. It is the clean way to count: counts.merge(word, 1, Integer::sum).

open as a page

What is EnumMap, how is it implemented, and why is it preferred over HashMap for enum keys?

level: middleimportance: should knowfreq 48%

basics

~20 s

EnumMap is a special map whose keys must be values of one enum type. Internally it's just an array indexed by each enum's position, so it's very fast and compact, and it keeps entries in the enum's declared order.

open as a page

What is IdentityHashMap and how does its notion of key equality differ from a normal HashMap?

level: middleimportance: should knowfreq 45%

basics

~10 s

IdentityHashMap treats two keys as the same only when they are literally the same object (==), not when they are merely equal (.equals()). So two distinct strings with identical text are two different keys.

open as a page

What data structure backs TreeMap, and why does it guarantee O(log n) operations?

level: middleimportance: should knowfreq 55%

basics

~20 s

TreeMap is backed by a red-black tree, a self-balancing binary search tree. Because the tree keeps itself roughly balanced, its height stays around log n, so finding, inserting, or removing a key takes O(log n) steps.

open as a page

How does HashMap compute a bucket index from a key, and why does it XOR the high bits of the hash?

level: seniorimportance: should knowfreq 50%

basics

~20 s

HashMap picks a bucket with (capacity - 1) & hash, a fast bitmask that works because capacity is a power of two. But that mask only looks at the low bits, so HashMap first mixes each key's high bits down by XOR-ing hash with (hash >>> 16). This spreads keys more evenly and avoids clustering.

open as a page

What is bucket treeification in HashMap (Java 8+), and under what exact conditions does it happen?

level: seniorimportance: should knowfreq 55%

basics

~20 s

In Java 8+, if too many keys collide into one bucket, HashMap turns that bucket's linked list into a balanced red-black tree so lookups in it become O(log n) instead of O(n). It happens when a bucket reaches 8 entries, but only if total capacity is at least 64.

open as a page

Explain the contract of removeEldestEntry: when it's invoked, what 'eldest' means, and what returning true does.

level: seniorimportance: should knowfreq 45%

basics

~10 s

removeEldestEntry is a hook LinkedHashMap calls after each insertion, passing the oldest entry in its list. If your override returns true, LinkedHashMap removes that oldest entry; the default returns false, so nothing is auto-removed.

open as a page

Why are the compute/merge family the correct tools for thread-safe accumulation on a ConcurrentHashMap, and what are the pitfalls?

level: seniorimportance: should knowfreq 48%

basics

~10 s

On a ConcurrentHashMap, compute, computeIfAbsent, computeIfPresent, and merge perform the read-modify-write for a key as one atomic step, so two threads can't lose each other's updates. Plain get-then-put can; that's the bug they fix.

open as a page

Given Hashtable, IdentityHashMap, WeakHashMap, and EnumMap, how do you decide which specialty map fits a problem?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Pick by the special requirement: EnumMap for enum keys (fast, ordered), WeakHashMap when entries should disappear once keys are unused, IdentityHashMap when keys must match by identity (==), and avoid Hashtable (legacy) — use HashMap or ConcurrentHashMap.

open as a page

What is WeakHashMap, what makes its keys garbage-collectible, and what is a common use for it?

level: seniorimportance: should knowfreq 50%

basics

~20 s

WeakHashMap holds its keys with weak references, so once nothing else points to a key, the garbage collector can remove it and its entry disappears automatically. It's handy for caches that should not keep objects alive.

open as a page

Why must a TreeMap's Comparator be consistent with equals, and what bugs appear when it is not?

level: seniorimportance: should knowfreq 42%

basics

~20 s

TreeMap decides whether two keys are 'the same' using compare()/compareTo, not equals(). If your Comparator says two different objects compare as 0, TreeMap treats them as one key — so puts overwrite and lookups can 'miss' keys, breaking the Map contract.

open as a page

How do headMap, tailMap, and subMap work, and what does it mean that they return live views with inclusive/exclusive bounds?

level: seniorimportance: should knowfreq 50%

basics

~20 s

They return sub-portions of the TreeMap by key range: headMap = keys below a bound, tailMap = keys at/above a bound, subMap = keys between two bounds. The overloads with a boolean let you include or exclude the endpoints. The result is a live view, not a copy — changes go both ways.

open as a page

What are the concurrency and design trade-offs of a LinkedHashMap-based LRU cache, and when would you choose a dedicated cache library instead?

level: principalimportance: nice to knowfreq 35%

basics

~20 s

A LinkedHashMap LRU is simple but not thread-safe, and even reads mutate it in access-order mode, so concurrent use needs full locking. For real systems pick a library like Caffeine that gives concurrent access, expiry, and size weighting.

open as a page

When should you choose TreeMap over HashMap, and what alternatives exist for sorted or concurrent ordered access?

level: principalimportance: nice to knowfreq 38%

basics

~20 s

Use TreeMap when you need keys in sorted order or range/nearest-key queries. Otherwise prefer HashMap for speed. For thread-safe sorted access use ConcurrentSkipListMap; for fixed sorted data, a sorted array with binary search can be faster and lighter.

open as a page