Initialize Map in Java
This post will discuss various methods to initialize a map in Java.
Java is often criticized for its verbosity. For example, creating a map containing involves constructing it, storing it in a variable, invoking the put() method on it several times, and then maybe wrapping it to make it unmodifiable:
|
1 2 3 4 5 6 7 |
Map<String, String> hm = new HashMap<>(); hm.put("USA", "Washington"); hm.put("United Kingdom", "London"); hm.put("India", "New Delhi"); hm = Collections.unmodifiableMap(hm); |
This post will discuss various methods to initialize a map in a single expression.
1. Using Java Collections
The Collections class consists of several static factory methods that operate on collections and return a new collection backed by a specified collection.
⮚ Collections.unmodifiableMap()
The Collections class provides an unmodifiableMap() method that takes another map and wraps it in a class that rejects modification requests, thus creating an unmodifiable view of the original map. Any attempts to modify the returned map will result in an UnsupportedOperationException.
|
1 2 |
Map<String, String> map = Collections.EMPTY_MAP; Map<String, String> unmodifiableMap = Collections.unmodifiableMap(map); |
But it won’t create an inherently unmodifiable map. Instead, possession of a reference to the underlying collection still allows modification. Also, each wrapper is an additional object, requiring another level of indirection and consuming more memory than the original collection. Finally, the wrapped collection still bears the expense of supporting mutation even if it is never intended to be modified.
⮚ Collections.singletonMap()
There are existing factories in the Collections class to produce a singleton Map with exactly one key-value pair.
We can use Collections.singletonMap() that returns an immutable map containing specified key-value pair. The map will throw an UnsupportedOperationException if any modification operation is performed on it.
|
1 |
Map<String, String> immutableMap = Collections.singletonMap("USA", "Washington"); |
⮚ Collections.emptyMap()
We can use Collections.EMPTY_MAP that returns a serializable and immutable empty map.
|
1 |
Map<String, String> immutableMap = Collections.EMPTY_MAP; |
The above method might throw an unchecked assignment warning. The following example demonstrates the type-safe way to obtain an empty map:
|
1 |
Map<String, String> immutableMap = Collections.emptyMap(); |
2. Using Java 8
We can use the Java 8 Stream to construct small maps by obtaining stream from static factory methods like Stream.of() or Arrays.stream() and accumulating the input elements into a new map using collectors.
⮚ Collectors.toMap()
A stream is essentially a sequence of items, not a sequence of key/value pairs. It is illogical to think that we can just take a stream and construct a map out of it without specifying how to extract keys and values. We can use the Collectors.toMap() method to specify it.
Collectors.toMap() returns a Collector that accumulates elements into a map whose keys and values result from applying the provided mapping functions to the input elements.
|
1 2 3 4 5 |
Map<String, String> map = Stream.of(new String[][]{ {"USA", "Washington"}, {"United Kingdom", "London"}, {"India", "New Delhi"} }).collect(Collectors.toMap(p -> p[0], p -> p[1])); |
To initialize a map with different types for key and value, for instance Map<String, Integer>, we can do:
|
1 2 3 4 5 |
Map<String, Integer> map = Stream.of(new Object[][]{ {"USA", 1}, {"United Kingdom", 2}, {"India", 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. There are two implementations of the Map.Entry<K,V> interface:
a. AbstractMap.SimpleEntry<K,V>:
|
1 2 3 4 5 |
Map<String, String> map = Stream.of( new AbstractMap.SimpleEntry<>("USA", "Washington"), new AbstractMap.SimpleEntry<>("United Kingdom", "London"), new AbstractMap.SimpleEntry<>("India", "New Delhi")) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) |
b. AbstractMap.SimpleImmutableEntry<K,V>:
|
1 2 3 4 5 |
Map<String, String> map = Stream.of( new AbstractMap.SimpleImmutableEntry<>("USA", "Washington"), new AbstractMap.SimpleImmutableEntry<>("United Kingdom", "London"), new AbstractMap.SimpleImmutableEntry<>("India", "New Delhi")) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) |
⮚ Collectors.collectingAndThen()
We could also adapt a collector to perform an additional finishing transformation. For example, we could adapt the toMap() collector to always produce an immutable map with:
|
1 2 3 4 5 6 |
Map<String, String> immutableMap = Stream.of(new String[][]{ {"USA", "Washington"}, {"United Kingdom", "London"}, {"India", "New Delhi"} }).collect(Collectors.collectingAndThen(Collectors.toMap(p -> p[0], p -> p[1]), Collections::<String, String>unmodifiableMap)); |
3. Using Double Brace Initialization
Another alternative is to use “Double Brace Initialization”. This creates an anonymous inner class with an instance initializer in it. We should avoid this technique at all costs 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.
|
1 2 3 4 5 |
Map<String, String> map = new HashMap<String, String>() {{ put("USA", "Washington"); put("United Kingdom", "London"); put("India", "New Delhi"); }}; |
That’s all about initializing Map in Java.
Also See:
Thanks for reading.
To share your code in the comments, please use our online compiler that supports C, C++, Java, Python, JavaScript, C#, PHP, and many more popular programming languages.
Like us? Refer us to your friends and support our growth. Happy coding :)