Back to Blog
Java

Java Map.of: Creating Immutable Maps with Ease

java map of: Learn how Java Map.of creates immutable maps with fixed key-value pairs, its limits on nulls and size, and when to prefer it over other map implementations.

JavaMap.ofImmutable MapJava CollectionsStatic Factory Methods
A Java Map.of method call creating an immutable map with fixed key-value pairs, illustrated with a lock icon.

The java.util.Map.of static factory method, introduced in Java 9, provides a concise way to create immutable maps with a fixed set of key-value pairs. It is a common answer to the java map of query because it replaces verbose manual map construction and enforces immutability at the API level. This article explains how Map.of works, its constraints, and when it is the right choice.

What Map.of Provides

Map.of is an overloaded method that accepts zero or more key-value pairs. The simplest form creates an empty map:

Map<String, Integer> empty = Map.of();

With one pair, the type parameters are inferred from the arguments:

Map<String, Integer> one = Map.of("key", 1);

For two to ten pairs, you pass alternating keys and values:

Map<String, String> config = Map.of( "host", "localhost", "port", "8080", "timeout", "30" );

The returned map is immutable and contains exactly the entries you provided. No put, remove, or clear operation is allowed. The API is intentionally small and avoids the boilerplate of creating a HashMap and then wrapping it with Collections.unmodifiableMap.

Limits on Argument Count and Nulls

Map.of has two important constraints. First, it accepts at most ten key-value pairs. This is because the method is overloaded from zero to ten arguments, and Java does not support default parameters. If you need more than ten entries, use Map.ofEntries instead.

Second, neither keys nor values can be null. Passing a null key or value throws a NullPointerException. This restriction aligns with the design goal of an immutable map that should not contain ambiguous or mutable state. If your data requires null values, Map.of is not the right tool.

Map.of vs Map.ofEntries

For more than ten entries, Map.ofEntries accepts a varargs array of Map.Entry objects. You can create each entry with the static Map.entry method:

Map<Integer, String> large = Map.ofEntries( Map.entry(1, "one"), Map.entry(2, "two"), Map.entry(3, "three") // add as many as needed );

Map.ofEntries has no limit on the number of pairs, but it is slightly more verbose. Use it when the map size is dynamic or exceeds ten. Both methods produce immutable maps with the same null and duplicate key restrictions.

Immutability and Runtime Behavior

The map returned by Map.of is unmodifiable. Any attempt to modify it throws UnsupportedOperationException. This is a compile-time guarantee only in the sense that the API signature does not expose mutating methods; the exception occurs at runtime if you call them through a Map reference.

Duplicate keys are rejected at creation time. If you pass the same key twice, Map.of throws IllegalArgumentException. This prevents silent overwriting and makes the map definition explicit.

Iteration order is unspecified. The Map.of implementation may choose any order, and you should not rely on it. If you need a predictable order, use a LinkedHashMap and wrap it with Collections.unmodifiableMap if immutability is also required.

Choosing Between Map.of and Other Map Implementations

Map.of is best for small, fixed, compile-time-known mappings such as configuration constants, lookup tables, or default values. It is compact, immutable, and thread-safe because it cannot be modified.

A HashMap is appropriate when you need a mutable map, when you must store null values, or when the map is built dynamically at runtime. A LinkedHashMap preserves insertion order, which can be important for iteration. Collections.unmodifiableMap can wrap any map to prevent modifications, but it does not reject null keys or values and does not provide the same compact factory syntax.

The following table summarizes the key differences:

FeatureMap.ofHashMapLinkedHashMap
MutabilityImmutableMutableMutable
Null keys/valuesNot allowedAllowedAllowed
Maximum entries10 (or via ofEntries)No limitNo limit
Iteration orderUnspecifiedUnspecifiedInsertion order
Creation syntaxConcise factorynew HashMap<>()new LinkedHashMap<>()

Use Map.of when the map is a fixed set of constants. Use HashMap when you need to modify the map after creation. Use LinkedHashMap when order matters.

Common Mistakes and Edge Cases

A frequent mistake is passing duplicate keys. The following code throws IllegalArgumentException:

Map<String, Integer> bad = Map.of("a", 1, "a", 2);

Another mistake is using null in a map that should be immutable. This also fails:

Map<String, String> bad = Map.of("key", null);

An empty map can be created with Map.of(), which is equivalent to Collections.emptyMap(). Both return immutable empty maps, but Map.of() is part of the same factory family and reads consistently with other Map.of calls.

When you need more than ten entries, remember that Map.ofEntries is the correct choice. Forgetting this and trying to pass eleven arguments to Map.of results in a compile-time error because no matching overload exists.

When Map.of Is Not the Right Fit

Map.of is not a general-purpose map. If you need to build a map incrementally from user input, a HashMap is more practical. If you need to allow null values, Map.of cannot be used. If you need a specific iteration order, use LinkedHashMap. If the map must be serializable in a particular format, check the requirements of your serialization framework; Map.of's internal representation is not guaranteed to be serializable in the same way as a HashMap.

For large static maps, Map.ofEntries is the better choice, but it still enforces immutability and null restrictions. When the map size is unknown at compile time, prefer a mutable implementation and apply an unmodifiable wrapper only after the map is fully populated.