Initialize Map with Values in Java Initialize Map with Values in Java

Page content

In this tutorial, we’ll learn different ways to initialize a Map with values in Java.

Using Map.of() and Map.ofEntries()

It is possible to initialize a Map with values in a single expression if you are using Java 9 or higher version using Map.of() and Map.ofEntries() method. This is shortest possible way so far.

Map.of()

Java 9 provides mutiple Map.of() overloaded methods to initialize a Map with upto 10 key-value pairs.

Map<String, Integer> emptyMap = Map.of();
Map<String, Integer> singletonMap = Map.of("A", 1);
Map<String, Integer> map = Map.of("A", 1, "B", 2, "C", 3);
Map.ofEntries()

If you have more than 10 key-value pairs to initialize, then you should use Map.ofEntries() method. This method has no limit and you can define any number of key-value pairs.

Map<String, Integer> map = Map.ofEntries(
        Map.entry("A", 1),
        Map.entry("B", 2),
        Map.entry("C", 3),
        Map.entry("D", 4),
        Map.entry("E", 5),
        Map.entry("F", 6),
        Map.entry("G", 7),
        Map.entry("H", 8),
        Map.entry("I", 9),
        Map.entry("J", 10),
        Map.entry("K", 11),
        Map.entry("L", 12)
);

map.put("M", 13); // Throw UnsupportedOperationException
map.remove("A");  // Throw UnsupportedOperationException
Mutable Map

Thing to note that both Map.of() and Map.ofEntries() return an immutable map which means that adding or removing an element in Map result into java.lang.UnsupportedOperationException exception.

You can avoid this by creating a mutable map (by copying the immutable map to new HashMap) in this way:-

Map<String, Integer> mutableEmptyMap = new HashMap<>(Map.of());
Map<String, Integer> mutableSingletonMap = new HashMap<>(Map.of("A", 1));
Map<String, Integer> mutableMap = new HashMap<>(Map.ofEntries(
        Map.entry("A", 1),
        Map.entry("B", 2),
        Map.entry("C", 3),
        Map.entry("D", 4),
        Map.entry("E", 5),
        Map.entry("F", 6),
        Map.entry("G", 7),
        Map.entry("H", 8),
        Map.entry("I", 9),
        Map.entry("J", 10),
        Map.entry("K", 11),
        Map.entry("L", 12)
));

mutableMap.put("M", 13); // It works!
mutableMap.remove("A");  // It works!

Using Java Collections

Java Collections class provide methods to initialize emptyMap(), singletonMap() and unmodifiableMap(). Note that all these methods return immutable map

Map<String, Integer> emptyMap = Collections.emptyMap();
Map<String, Integer> singletonMap = Collections.singletonMap("A", 1);
        
singletonMap.put("B", 2); // Throw UnsupportedOperationException
singletonMap.remove("A"); // Throw UnsupportedOperationException

Map<String, Integer> mutableMap = new HashMap<>(singletonMap);
mutableMap.put("B", 2);  // It works!

Map<String, Integer> immutableMap = Collections.unmodifiableMap(mutableMap);
immutableMap.put("B", 2); // Throw UnsupportedOperationException

Initialize Map as an instance variable

If you initialize a Map as an instance variable, keep the initialization in a constructor or instance initializer:-

public class MyClass {

    Map<String, Integer> instanceMap = new HashMap<>();
    {
        instanceMap.put("A", 1);
        instanceMap.put("B", 2);
    }
}

Initialize Map as a static variable

If you initialize a Map as a static class variable, keep the initialization in a static initializer:-

public class MyClass {

    static Map<String, Integer> staticMap = new HashMap<>();
    static{
        staticMap.put("A", 1);
        staticMap.put("B", 2);
    }
}

Using Double Brace Initialization

You can initialize map with values using Double Brace Initialization:-

Map<String, Integer> map = new HashMap<>() {{
    put("A", 1);
    put("B", 2);
}};

In Double brace initialization {{ }}, first brace creates a new Anonymous Inner Class, the second brace declares an instance initializer block that is run when the anonymous inner class is instantiated.

This approach is not recommended as it creates an extra class at each usage. It also holds hidden references to the enclosing instance and any captured objects. This may cause memory leaks or problems with serialization.

The alternative approach for this is to create a function to initialize a map:-

// It works for all Java versions, mutable map.
Map<String, Integer> map = createMap();
map.put("C", "3");  // It works!

private static Map<String, String> createMap() {
    Map<String, Integer> map = new HashMap<>();
    map.put("A", 1);
    map.put("B", 2);
    return map;
}

Using Stream Collectors.toMap()

We can also use Java 8 Stream API to initialize a Map with values.

When both key and value are of same type (e.g. String):-

Map<String, String> mutableMap1 = Stream.of(new String[][]{
        {"A", "a"},
        {"B", "b"},
        {"C", "c"}
}).collect(Collectors.toMap(p -> p[0], p -> p[1]));

When both key and value are of different type (e.g. String and Integer):-

Map<String, Integer> mutableMap2 = Stream.of(new Object[][]{
        {"A", 1},
        {"B", 2},
        {"C", 3}
}).collect(Collectors.toMap(p -> (String) p[0], p -> (Integer) p[1]));

Another approach that can easily accommodate different types for key and value involves creating a stream of map entries.

Map<String, Integer> mutableMap3 = Stream.of(
        new AbstractMap.SimpleEntry<>("A", 1),
        new AbstractMap.SimpleEntry<>("B", 2),
        new AbstractMap.SimpleEntry<>("C", 3))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

Map<String, Integer> mutableMap4 = Stream.of(
      new AbstractMap.SimpleImmutableEntry<>("A", 1),
      new AbstractMap.SimpleImmutableEntry<>("B", 2),
      new AbstractMap.SimpleImmutableEntry<>("C", 3))
      .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));       

The only difference between SimpleEntry and SimpleImmutableEntry is that you can set the value of SimpleEntry instance once initialized whereas set value of SimpleImmutableEntry after initialization throw UnsupportedOperationException.

Note that all the maps we have initialized using streams so far are mutable map means we can add or remove elements from them. You can initialize an immutable map using streams in this way:-

 Map<String, Integer> map5 = Stream.of(
      new AbstractMap.SimpleEntry<>("A", 1),
      new AbstractMap.SimpleEntry<>("B", 2),
      new AbstractMap.SimpleEntry<>("C", 3))
      .collect(Collectors.collectingAndThen(
        Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue),
        Collections::unmodifiableMap
      ));

Conclusion

Let’s look at the summary of all the ways to initialize a Map with values:-

  1. Using Map.of() and Map.ofEntries() – Recommended this single line expression if you use Java 9 and above
  2. Using Java Collections – Works with all Java versions. Useful to define singleton map upto Java 8
  3. Using Double Brace Initialization - Avoid Double braces initialization. Create a method instead.
  4. Initialize Map as an instance variable - Recommended to initialize instance variable
  5. Initialize Map as a static variable - Recommended to initialize static variable
  6. Using Stream Collectors.toMap() – Too many lines of code. We can use other alternatives if possible to avoid boilerplate code.