Map.of()

Create a small unmodifiable map in one expression, and know why it rejects nulls, duplicate keys and any later change.

Signature
static <K, V> Map<K, V> of(K k1, V v1, ..., K k10, V v10)
  • Added in Java Java 9

Parameters

ParameterTypeDescription
$k1optionalKThe first key. Must not be null.
$v1optionalVThe value for k1. Must not be null.
$k2 ... v10optionalK, VFurther keys and values in pairs, up to ten pairs in total. Use Map.ofEntries for more.

Return value

Map<K, V>An unmodifiable map containing the given mappings. There are overloads for zero to ten key-value pairs.

Errors

IllegalArgumentException if any two keys are equal; NullPointerException if any key or value is null. Every mutating method on the result throws UnsupportedOperationException.

Map.of builds an unmodifiable map from keys and values written in pairs: Map.of("free", 60, "team", 600). It arrived in Java 9 and replaced the old routine of creating a HashMap, calling put a few times and wrapping it in Collections.unmodifiableMap.

There are overloads for zero to ten pairs. Keys and values alternate, so there is no varargs version. For more than ten entries use Map.ofEntries(Map.entry(k, v), ...), which takes any number of entries.

What it refuses

  • Duplicate keys throw IllegalArgumentException when the map is created. A lookup table with a copy-and-paste mistake fails at startup instead of silently keeping one of the values.
  • Null keys and null values throw NullPointerException. If a value comes from an environment variable that is not set, Map.of fails on the spot.
  • Changes after creation. put, remove, merge, clear and the other mutators throw UnsupportedOperationException. The map is only shallowly immutable: a mutable object stored as a value can still be changed.
  • Null queries. get(null) and containsKey(null) throw NullPointerException rather than returning null or false. Check a request parameter for null before using it as a key.

Iteration order is not yours to rely on

The iteration order of Map.of is unspecified and can change from one run of the JVM to the next. Code that prints such a map, serialises it to JSON or compares its toString() in a test can pass once and fail later. When order matters, copy into a TreeMap for sorted keys or build a LinkedHashMap for insertion order.

Of, copyOf and unmodifiableMap

Map.copyOf (Java 10) takes a snapshot of an existing map with the same rules. Collections.unmodifiableMap is different: it is a read-only view, so later changes to the underlying map show through it. Return Map.copyOf(internal) from a service when callers must not see later updates.

On the back end Map.of suits fixed tables: status codes to reason phrases, file extensions to content types, default headers and test fixtures.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Mapping HTTP status codes to reason phrases in an error response builder.
  • Declaring a fixed table of file extensions to content types for a static file handler.
  • Building small fixture maps of request parameters for unit tests of a service method.

Frequently asked questions

How many entries can Map.of take?

Up to ten key-value pairs; there is an overload for each count from zero to ten. For more, use Map.ofEntries(Map.entry(k, v), ...), which accepts any number of entries.

Why does Map.of throw NullPointerException?

Maps built by Map.of do not allow null keys or values, and they also throw when you query them with a null key, for example get(null) or containsKey(null). Leave the entry out instead of storing null, or use a HashMap if you really need null.

Does Map.of keep insertion order?

No. Its iteration order is unspecified and can change between JVM runs. Use a LinkedHashMap for insertion order or a TreeMap for sorted keys.

More examples

Duplicate keys and nulls fail at creation

All three mistakes fail fast with an exception instead of producing a map that quietly holds the wrong data. The containsKey(null) case surprises people who are used to HashMap, which simply answers false.

Example · java
void main() {
    try {
        Map.of("timeout", 30, "retries", 3, "timeout", 60);
    } catch (IllegalArgumentException e) {
        IO.println("IllegalArgumentException: " + e.getMessage());
    }

    String region = null; // for example, an environment variable that is not set
    try {
        Map.of("region", region);
    } catch (NullPointerException e) {
        IO.println("NullPointerException: null value");
    }

    try {
        Map.of("page", "1").containsKey(null);
    } catch (NullPointerException e) {
        IO.println("NullPointerException: containsKey(null)");
    }
}
// Output:
// IllegalArgumentException: duplicate key: timeout
// NullPointerException: null value
// NullPointerException: containsKey(null)

More than ten entries with Map.ofEntries

Map.of stops at ten pairs, so an eleventh entry needs Map.ofEntries. A static import of Map.entry keeps the table readable.

Example · java
import static java.util.Map.entry;

void main() {
    Map<String, String> contentTypes = Map.ofEntries(
            entry("html", "text/html"),
            entry("css", "text/css"),
            entry("js", "text/javascript"),
            entry("json", "application/json"),
            entry("xml", "application/xml"),
            entry("png", "image/png"),
            entry("jpg", "image/jpeg"),
            entry("gif", "image/gif"),
            entry("svg", "image/svg+xml"),
            entry("pdf", "application/pdf"),
            entry("txt", "text/plain"));

    IO.println(contentTypes.size());
    IO.println(contentTypes.get("json"));
    IO.println(contentTypes.getOrDefault("exe", "application/octet-stream"));
}
// Output:
// 11
// application/json
// application/octet-stream

A snapshot with Map.copyOf versus a view

The unmodifiable view follows every later change to the map behind it, while Map.copyOf took a copy at that moment. Use copyOf when handing configuration to other code that must not see later edits.

Example · java
void main() {
    Map<String, Integer> limits = new HashMap<>();
    limits.put("free", 100);
    limits.put("team", 1000);

    Map<String, Integer> view = Collections.unmodifiableMap(limits);
    Map<String, Integer> snapshot = Map.copyOf(limits);

    limits.put("enterprise", 10000);

    IO.println("view sees: " + view.size());
    IO.println("snapshot sees: " + snapshot.size());
    IO.println(snapshot.containsKey("enterprise"));
}
// Output:
// view sees: 3
// snapshot sees: 2
// false

Discussion

  • Be the first to comment on this lesson.