Generic Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Introduction

Java 8 introduced the Map.of() method, which makes it easier to create immutable maps. And Java 9 gained the Map.ofEntries() method, which has slightly different functionality.

In this tutorial, we'll take a closer look at these two static factory methods for immutable maps and explain which one is suitable for which purpose.

2. Map.of()

The Map.of() method takes a specified number of key-value pairs as arguments and returns an immutable map containing each key-value pair. The order of the pairs in the arguments corresponds to the order in which they are added to the map. If we try to add a key-value pair with a duplicate key, it'll throw an IllegalArgumentException. If we attempt to add a null key or value, it will throw a NullPointerException.

Implemented as overloaded static factory methods, the first one lets us create an empty map:

static <K, V> Map<K, V> of() {
    return (Map<K,V>) ImmutableCollections.EMPTY_MAP;
}

Let's see the usage:

Map<Long, String> map = Map.of();

There's also a method defined in the interface of Map<K, V> that takes a single key and value:

static <K, V> Map<K, V> of(K k1, V v1) {
    return new ImmutableCollections.Map1<>(k1, v1);
}

Let's call it:

Map<Long, String> map = Map.of(1L, "value1");

Those factory methods are overloaded nine more times, accepting up to ten keys and ten values, as we can find in OpenJDK 17:

static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10) {
    return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10);
}

Even though these methods are quite useful, it would be a mess to create a lot more of them. Also, we can't use the Map.of() method to create a map from existing keys and values because this method only accepts undefined key-value pairs as arguments. This is where the Map.ofEntries() method comes in.

3. Map.ofEntries()

The Map.ofEntries() method takes an unspecified number of Map.Entry<K, V> objects as arguments and also returns an immutable map. Again, the order of the pairs in the arguments is the same as the order in which they are added to the map. If we try to add a key-value pair with a duplicate key, it throws an IllegalArgumentException.

Let's look at the static factory method implementation according to OpenJDK 17:

static <K, V> Map<K, V> ofEntries(Entry<? extends K, ? extends V>... entries) {
    if (entries.length == 0) { // implicit null check of entries array
        var map = (Map<K,V>) ImmutableCollections.EMPTY_MAP;
        return map;
    } else if (entries.length == 1) {
        // implicit null check of the array slot
        return new ImmutableCollections.Map1<>(entries[0].getKey(), entries[0].getValue());
    } else {
        Object[] kva = new Object[entries.length << 1];
        int a = 0;
        for (Entry<? extends K, ? extends V> entry : entries) {
            // implicit null checks of each array slot
            kva[a++] = entry.getKey();
            kva[a++] = entry.getValue();
        }
        return new ImmutableCollections.MapN<>(kva);
     }
}

The variable arguments implementation allows us to pass a variable amount of entries.

For example, we can create an empty map:

Map<Long, String> map = Map.ofEntries();

Or we can create and populate a map:

Map<Long, String> longUserMap = Map.ofEntries(Map.entry(1L, "User A"), Map.entry(2L, "User B"));

A big advantage of the Map.ofEntries() method is that we can also use it to create a map from existing keys and values. This isn't possible with the Map.of() method because it only accepts undefined key-value pairs as arguments.

4. Conclusion

The Map.of() method is only suitable for maps with a maximum of 10 elements. That's because it's implemented as 11 distinct overloaded methods that take from zero to ten name-value pairs as arguments. The Map.ofEntries() method, on the other hand, can be used for maps of any size because it takes advantage of the feature variable arguments.

The complete examples are available over on GitHub.

Generic bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Generic footer banner
Comments are closed on this article!