Immutable Map in Java
Problem
Map.of() (Java 9+) builds a Map that can never be modified after creation, following the same immutability guarantee as List.of() and Set.of().
Create a Map of key-value pairs that can never be added to, removed from, or changed after it's built.
Java Program
import java.util.Map;
public class ImmutableMapDemo {
public static void main(String[] args) {
Map<String, Integer> scores = Map.of("a", 1, "b", 2, "c", 3);
System.out.println("Value for b: " + scores.get("b"));
// scores.put("d", 4) here would throw UnsupportedOperationException
}
}Output
Core Logic
Passing alternating key-value arguments to Map.of() builds an immutable map directly, without a mutable HashMap ever existing in between.
- 1
Map.of("a", 1, "b", 2, "c", 3)takes its arguments as alternating key, value, key, value pairs. - 2The returned map supports normal reads —
get(),containsKey(), iteration — exactly like a regular Map. - 3Calling a mutating method like
.put(...)throwsUnsupportedOperationException, the same guarantee as List.of() and Set.of(). - 4Duplicate keys among the arguments throw
IllegalArgumentExceptionat construction, just as Set.of() rejects duplicate elements.
Map.of("a", 1, "b", 2, "c", 3).get("b") returns 2.Key Point: The alternating key-value argument list gets unwieldy for a map with many entries — Map.ofEntries(Map.entry("a", 1), Map.entry("b", 2), ...) is the alternative for those cases, taking a list of key-value pairs instead of one long flat argument list.
Why: Map.of() stores each of the n given key-value entries once, after checking for duplicate keys among them.
Key Concepts
Approach 2: Java 8
import java.util.Map;
import java.util.stream.Collectors;
public class ImmutableMapStream {
public static void main(String[] args) {
Map<String, Integer> source = Map.of("a", 1, "b", 2, "c", 3);
// Doubles each value while collecting into a genuinely immutable map
Map<String, Integer> doubled = source.entrySet().stream()
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, e -> e.getValue() * 2));
System.out.println("Value for b: " + doubled.get("b"));
// doubled.put("d", 8) here would throw UnsupportedOperationException
}
}
Output
Core Logic
Collectors.toUnmodifiableMap() builds an immutable map from a stream pipeline directly, for the common case where the entries are derived from existing data rather than being known ahead of time as literals.
- 1
source.entrySet().stream()opens a stream over an existing mutable map's entries. - 2
Collectors.toUnmodifiableMap(Map.Entry::getKey, e -> e.getValue() * 2)builds a new map, keeping each original key but doubling its value — somethingMap.of()can't express, since it just takes fixed key-value pairs directly. - 3The result carries the same immutability guarantee as
Map.of()— aput()call on it throwsUnsupportedOperationException. - 4The original
sourcemap is left completely unmodified.
{a=1, b=2, c=3} and collecting produces the immutable map {a=2, b=4, c=6}, so get("b") returns 4.Key Point: Map.of() is for entries already known as literals; Collectors.toUnmodifiableMap() is for the more common real-world case — an immutable map built from data that was transformed or derived from another map first.
Why: The stream visits each of the source map's n entries once, and collect() builds a new immutable map to hold the transformed entries.