In this post, we will discuss various methods to create immutable map in Java.
Unmodifiable Maps are “read-only” wrappers over other collections. They do not support any modification operations such as add, remove and clear but their underlying collection can be changed. Maps that additionally guarantee that no change in the Collection object will ever be visible are referred to as immutable.
When you don’t expect to modify a collection, it’s a good practice to defensively copy it into an immutable collection. Immutable Maps have many advantages over their mutable siblings, like they are thread-safe, more memory-efficient and can be passed to untrusted libraries without any side effects.
1. Guava
The Collections framework provides unmodifiableMap()
method, but it is unsafe to use as the returned map is only truly immutable if nobody holds a reference to the original collection. The returned map is also inefficient as the data structures will still have all the overhead of mutable collections, including concurrent modification checks, extra space in hash tables, etc.
Guava provides simple, easy-to-use immutable versions of each Map
using ImmutableMap
class. Unlike Collections’s unmodifiableMap()
an instance of ImmutableMap
contains its own private data and will never change. An ImmutableMap
collection can be created in several ways:
1. Using the copyOf() method
ImmutableMap.copyOf
returns an immutable map containing the same entries as specified map. It returns a NullPointerException if any key or value in map is null.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import com.google.common.collect.ImmutableMap; import java.util.HashMap; import java.util.Arrays; import java.util.Map; class MapUtil { // Immutable Map in Java public static void main(String[] args) { Map<String, String> mutableMap = new HashMap<>(); mutableMap.put("United States", "Washington D.C."); // ImmutableMap.copyOf() creates a copy of the mutable map ImmutableMap<String, String> immutableMap = ImmutableMap .copyOf(mutableMap); try { // any attempt to modify the map will result in // an UnsupportedOperationException immutableMap.put("United Kingdom", "London"); } catch (UnsupportedOperationException ex) { System.out.println("java.lang.UnsupportedOperationException"); } // any changes made to the original map will not be reflected // in the immutable map mutableMap.put("India", "New Delhi"); System.out.println(immutableMap); } } |
Output:
java.lang.UnsupportedOperationException
{United States=Washington D.C.}
2. Using a Builder
Guava also provides a builder for creating immutable map instances as shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
import com.google.common.collect.ImmutableMap; import java.util.HashMap; import java.util.Arrays; import java.util.Map; class MapUtil { // Immutable Map in Java public static void main(String[] args) { Map<String, String> mutableMap = new HashMap<>(); mutableMap.put("United States", "Washington D.C."); // 1. get a builder for creating immutable map instances // 2. putAll() adds all entries of mutable map to the ImmutableMap // 3. put() associates key with value in the built map // 4. build() returns a newly-created ImmutableMap ImmutableMap<String, String> immutableMap = = ImmutableMap.<String, String>builder() .putAll(mutableMap) .put("China", "Beijing") .build(); try { // any attempt to modify the map will result in // an UnsupportedOperationException immutableMap.put("United Kingdom", "London"); } catch (UnsupportedOperationException ex) { System.out.println("java.lang.UnsupportedOperationException"); } // any changes made to the original map will not be reflected // in the immutable map mutableMap.put("India", "New Delhi"); System.out.println(immutableMap); } } |
Output:
java.lang.UnsupportedOperationException
{United States=Washington D.C., China=Beijing}
3. Using the of method
ImmutableMap.of()
returns an immutable map containing the given entries, in order. It throws an IllegalArgumentException if duplicate keys are provided. This works for up to 5 key/value pairs. To create a map with an arbitrary number of entries, we can use Builder’s put()
method which takes key-value pairs instead of a map.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import com.google.common.collect.ImmutableMap; class MapUtil { public static void main(String[] args) { // create an immutable map containing the given entries, in order ImmutableMap<String, String> immutableMap = null; immutableMap = ImmutableMap.of("United States", "Washington D.C."); try { // any attempt to modify the map will result in // an UnsupportedOperationException immutableMap.put("United Kingdom", "London"); } catch (UnsupportedOperationException ex) { System.out.println("java.lang.UnsupportedOperationException"); } System.out.println(immutableMap); } } |
Output:
java.lang.UnsupportedOperationException
{United States=Washington D.C.}
2. Java 9 – Collection factory methods
Several collection factory methods have been added in Java 9 that creates compact, structurally immutable instance of Map. For example,
1 2 3 4 5 6 |
Map.of() // creates an empty map Map.of(k1, v1) // creates singleton Map Map.of(k1, v1, k2, v2) Map.of(k1, v1, k2, v2, k3, v3) ... |
This supports maps upto 10 key-value pairs. There is a no var-args overload of Map.of()
which can handle any number of mappings. To create a map with an arbitrary number of entries, we can use
1 |
Map.ofEntries(Map.Entry<K,V>...) |
It include varargs overloads, so there is no fixed limit on the map size. This approach requires each key-value pair to be boxed. For boxing keys and values, we can use
1 |
Map.Entry<K,V> entry(K k, V v) |
Here’s complete usage of this method:
1 2 3 4 5 6 7 |
Map.ofEntries( entry(k1, v1), entry(k2, v2), entry(k3, v3), // ... entry(kn, vn) ); |
As map is structurally immutable, keys and values cannot be added, removed, or updated from it but if the contained keys or values are themselves mutable, this may cause the Map to behave inconsistently or its contents to appear to change.
References: Guava’s Wiki – Immutable Collections Explained
Thanks for reading.
Please use our online compiler to post code in comments. To contribute, get in touch with us.
Like us? Please spread the word and help us grow. Happy coding 🙂
Leave a Reply
How to convert Immutable map to mutable map. Is looping over and adding new fields to mutable map is the only solution.