Map Interface
The Map interface is your go-to tool whenever you need to associate keys with values — think dictionaries, phone books, or configuration tables. Unlike the rest of the collections framework, Map does not extend Collection; it lives in its own corner of java.util and has its own rules.

What Is the Map Interface?
A Map<K, V> stores entries, where each entry is a unique key mapped to a value. Keys must be unique; values do not have to be. If you put a second value under an existing key, the old value is replaced.
import java.util.HashMap;
import java.util.Map;
public class MapBasics {
public static void main(String[] args) {
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 95);
scores.put("Bob", 87);
scores.put("Carol", 92);
System.out.println("Bob's score: " + scores.get("Bob"));
System.out.println("Size: " + scores.size());
System.out.println(scores);
}
}
Output:
Bob's score: 87
Size: 3
{Alice=95, Bob=87, Carol=92}
Note:
Mapis injava.util. Importjava.util.Mapplus the specific implementation you plan to use (HashMap,TreeMap, etc.).
The Map Hierarchy
Map is the root interface. Java ships several ready-to-use implementations, each optimized for a different scenario:
Map<K, V>
├── HashMap — fast, unordered
├── LinkedHashMap — insertion-ordered
├── TreeMap — sorted by key
├── Hashtable — legacy, synchronized
└── EnumMap — keys must be an enum type
| Implementation | Ordered? | Null keys | Null values | Thread-safe? | Performance |
|---|---|---|---|---|---|
HashMap | No | 1 allowed | Yes | No | O(1) avg |
LinkedHashMap | Insertion order | 1 allowed | Yes | No | O(1) avg |
TreeMap | Sorted (natural / Comparator) | No | Yes | No | O(log n) |
Hashtable | No | No | No | Yes (legacy) | O(1) avg |
For thread-safe access in modern code, prefer ConcurrentHashMap from Concurrent Collections over the old Hashtable.
Core Methods
| Method | Description |
|---|---|
put(K key, V value) | Inserts or replaces the mapping; returns the old value (or null) |
get(Object key) | Returns the value for the key, or null if absent |
getOrDefault(Object key, V def) | Returns the value, or def if the key is missing |
containsKey(Object key) | Returns true if the key exists |
containsValue(Object value) | Returns true if the value exists (O(n) scan) |
remove(Object key) | Removes the mapping; returns the old value |
remove(Object key, Object value) | Removes only if the key maps exactly to that value |
size() | Number of key-value pairs |
isEmpty() | Returns true if the map has no entries |
clear() | Removes all entries |
putIfAbsent(K key, V value) | Puts only if the key is not already mapped |
replace(K key, V value) | Replaces only if the key already exists |
keySet() | Returns a Set<K> of all keys |
values() | Returns a Collection<V> of all values |
entrySet() | Returns a Set<Map.Entry<K,V>> — the best way to iterate |
Working With the Methods
Putting and Getting Values
import java.util.HashMap;
import java.util.Map;
public class PutGet {
public static void main(String[] args) {
Map<String, String> capitals = new HashMap<>();
capitals.put("France", "Paris");
capitals.put("Japan", "Tokyo");
capitals.put("India", "New Delhi");
// get — returns null if key absent
System.out.println(capitals.get("Japan")); // Tokyo
System.out.println(capitals.get("Brazil")); // null
// getOrDefault — safe fallback
System.out.println(capitals.getOrDefault("Brazil", "Unknown")); // Unknown
// put replaces existing
String old = capitals.put("Japan", "Kyoto");
System.out.println("Old value: " + old); // Tokyo
System.out.println("New value: " + capitals.get("Japan")); // Kyoto
}
}
Output:
Tokyo
null
Unknown
Old value: Tokyo
New value: Kyoto
Checking for Keys and Values
import java.util.*;
public class ContainsDemo {
public static void main(String[] args) {
Map<String, Integer> inventory = new HashMap<>();
inventory.put("apples", 50);
inventory.put("bananas", 30);
System.out.println(inventory.containsKey("apples")); // true
System.out.println(inventory.containsKey("grapes")); // false
System.out.println(inventory.containsValue(30)); // true
}
}
Tip:
containsKeyis O(1) forHashMapandLinkedHashMap.containsValuealways scans all values — O(n) — regardless of implementation.
Iterating Over a Map
The three common iteration patterns:
import java.util.*;
public class IterateMap {
public static void main(String[] args) {
Map<String, Integer> ages = new LinkedHashMap<>();
ages.put("Alice", 30);
ages.put("Bob", 25);
ages.put("Carol", 28);
// 1. entrySet — key + value together (most efficient)
for (Map.Entry<String, Integer> entry : ages.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
// 2. keySet — when you only need keys
for (String name : ages.keySet()) {
System.out.println(name);
}
// 3. forEach (Java 8+) — cleanest
ages.forEach((name, age) -> System.out.println(name + " is " + age));
}
}
Output:
Alice -> 30
Bob -> 25
Carol -> 28
Alice
Bob
Carol
Alice is 30
Bob is 25
Carol is 28
Tip: Prefer
entrySet()over callingget(key)inside akeySet()loop. Getting both key and value from a singleEntryobject is faster than two separate lookups.
putIfAbsent and computeIfAbsent
These two methods are perfect for building frequency maps, grouping results, and initializing defaults without verbose null-checks.
import java.util.*;
public class ComputeDemo {
public static void main(String[] args) {
String[] words = {"apple", "banana", "apple", "cherry", "banana", "apple"};
Map<String, Integer> freq = new HashMap<>();
for (String word : words) {
// merge: if key absent, set to 1; else apply the function
freq.merge(word, 1, Integer::sum);
}
System.out.println(freq);
// computeIfAbsent: initialize a list only when needed
Map<String, List<String>> groups = new HashMap<>();
groups.computeIfAbsent("fruits", k -> new ArrayList<>()).add("mango");
groups.computeIfAbsent("fruits", k -> new ArrayList<>()).add("kiwi");
System.out.println(groups);
}
}
Output:
{banana=2, cherry=1, apple=3}
{fruits=[mango, kiwi]}
Removing Entries
import java.util.*;
public class RemoveDemo {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
// Remove by key
map.remove("a");
// Conditional remove — only removes if key maps to exactly this value
boolean removed = map.remove("b", 99); // false, value is 2 not 99
System.out.println("Conditional remove: " + removed);
System.out.println(map);
}
}
Output:
Conditional remove: false
{b=2, c=3}
Creating Immutable Maps (Java 9+)
import java.util.Map;
public class ImmutableMap {
public static void main(String[] args) {
// Up to 10 entries — factory method
Map<String, Integer> config = Map.of(
"timeout", 30,
"retries", 3,
"maxConnections", 100
);
System.out.println(config.get("timeout")); // 30
// config.put("newKey", 5); // throws UnsupportedOperationException
}
}
Note:
Map.of()introduced in Java 9 does not allownullkeys or values, and it does not guarantee iteration order. For more than 10 entries, useMap.ofEntries(Map.entry(k, v), ...).
Choosing the Right Implementation
import java.util.*;
public class MapChoice {
public static void main(String[] args) {
// HashMap — fastest, no order guarantee
Map<String, Integer> hashMap = new HashMap<>();
// LinkedHashMap — remembers insertion order
Map<String, Integer> linked = new LinkedHashMap<>();
// TreeMap — keys sorted alphabetically (natural order)
Map<String, Integer> tree = new TreeMap<>();
for (Map<String, Integer> m : List.of(hashMap, linked, tree)) {
m.put("banana", 2);
m.put("apple", 1);
m.put("cherry", 3);
System.out.println(m.getClass().getSimpleName() + ": " + m);
}
}
}
Output:
HashMap: {banana=2, cherry=3, apple=1}
LinkedHashMap: {banana=2, apple=1, cherry=3}
TreeMap: {apple=1, banana=2, cherry=3}
You can also supply a custom sort to TreeMap using a Comparator:
Map<String, Integer> reverseOrder = new TreeMap<>(Comparator.reverseOrder());
reverseOrder.put("apple", 1);
reverseOrder.put("banana", 2);
reverseOrder.put("cherry", 3);
System.out.println(reverseOrder); // {cherry=3, banana=2, apple=1}
Under the Hood
HashMap Internals
A HashMap internally uses an array of “buckets”. When you call put(key, value):
- Java calls
key.hashCode()to compute a hash. - The hash is spread across the bucket array using a bit-manipulation formula.
- The entry is stored in that bucket. If two keys land in the same bucket (a collision), they are chained together — as a linked list in older versions, and as a balanced red-black tree when the chain grows beyond 8 nodes (since Java 8).
Retrieval (get) follows the same hash path, then checks equals() to find the exact key within the bucket.
This is why two rules matter for any class you use as a Map key:
- If you override
equals(), you must overridehashCode()consistently. - Equal objects must produce the same hash code, or
get()will silently fail to find entries you just put.
See Working of HashMap for a deep visual walkthrough of buckets, load factor, and rehashing.
TreeMap Internals
TreeMap stores entries in a red-black tree — a self-balancing binary search tree. Every put, get, and remove is O(log n). In exchange, you get sorted keys for free, plus navigation methods like firstKey(), lastKey(), headMap(), and tailMap() that HashMap cannot offer.
The Map.Entry<K, V> Interface
When you call entrySet(), you get a live Set of Map.Entry objects. Each Entry holds a reference to the real key and value inside the map. This set is backed by the map — iterating it is as efficient as possible, and changes to the map are reflected immediately.
Warning: Do not modify a map’s structure (add or remove entries) while iterating over its
entrySet(),keySet(), orvalues(). Doing so throwsConcurrentModificationException. Useentry.setValue()to change a value in-place, or collect changes and apply them after the loop.
Memory Layout
A HashMap with default settings starts with 16 buckets and a load factor of 0.75. When 75% of the buckets are occupied, it rehashes — creates a new array double the size and redistributes all entries. Rehashing is an O(n) operation. If you know in advance that your map will hold n entries, construct it with new HashMap<>(n * 2) to avoid mid-flight rehashing.
Related Topics
- HashMap — the most common
Mapimplementation, with full examples - Working of HashMap — internals: buckets, hashing, collisions, and rehashing
- LinkedHashMap — insertion-ordered map with predictable iteration
- TreeMap — sorted map backed by a red-black tree
- Comparable and Comparator — control how
TreeMaporders its keys - Collections Framework — bird’s-eye view of when to use each data structure