Guava ImmutableMap.copyOf() method in Java
This post will discuss how to create an immutable map from another map in Java using Guava ImmutableMap.copyOf() method.
An immutable map is a type of map that cannot be modified after it is created. In this post, we will show you how to use the copyOf() method of the ImmutableMap class in Guava to create an immutable map from another map. We will also explain why this method is useful, how it works internally, and what are some of the benefits and drawbacks of using it.
1. Problem with Collections.unmodifiableMap() method
One of the common operations that you might want to perform on a map is creating an immutable copy of it. For example, you might have a mutable map that contains some configuration settings, and you want to create a constant map that contains the same settings. Or you might have a method that accepts a map as a parameter, and you want to create a defensive copy of it to prevent accidental or malicious modifications.
However, if you are using the standard Java collections API, you might find it difficult and inefficient to do so. For example, you might try to use the Collections.unmodifiableMap() method to create an unmodifiable view of another map. However, this method does not create a true copy of the original map, but rather a wrapper around it. This means that any changes to the original map will be reflected in the unmodifiable view, and vice versa. For example:
|
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 |
import java.util.Collections; import java.util.HashMap; import java.util.Map; class Main { public static void main(String[] args) { // Create a mutable map of colors Map<String, String> colors = new HashMap<>(); colors.put("red", "#FF0000"); colors.put("green", "#00FF00"); colors.put("blue", "#0000FF"); // Create an unmodifiable view of the colors map Map<String, String> unmodifiableColors = Collections.unmodifiableMap(colors); // {red=#FF0000, green=#00FF00, blue=#0000FF} System.out.println(unmodifiableColors); // Modify the original colors map colors.put("yellow", "#FFFF00"); // {red=#FF0000, green=#00FF00, blue=#0000FF, yellow=#FFFF00} System.out.println(unmodifiableColors); } } |
As you can see, modifying the original colors map affects the unmodifiable colors map, even though it is supposed to be unmodifiable. This is because the unmodifiable view is still backed by the original map, and does not have its own copy of the data.
2. Create an immutable map from another map
So how can you create an immutable copy of another map without modifying it? The answer is to use the copyOf() method of the ImmutableMap class in Guava. This method returns a new immutable map containing the same entries as another map. The method has the following signature:
|
1 2 |
public static <K,V> ImmutableMap<K,V> copyOf(Map<? extends K,? extends V> map) public static <K,V> ImmutableMap<K,V> copyOf(Iterable<? extends Map.Entry<? extends K,? extends V>> entries) |
The method takes a map of key-value pairs as a parameter or a view of the mappings, and returns an immutable map that contains the same entries as the input map. For example, you can use the ImmutableMap.copyOf() method to create an immutable map of strings from a HashMap as follows:
|
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; import java.util.HashMap; import java.util.Map; class Main { public static void main(String[] args) { // Create a mutable map of colors Map<String, String> colors = new HashMap<>(); colors.put("red", "#FF0000"); colors.put("green", "#00FF00"); colors.put("blue", "#0000FF"); // Create an immutable copy of the colors map ImmutableMap<String, String> immutableColors = ImmutableMap.copyOf(colors); // {red=#FF0000, green=#00FF00, blue=#0000FF} System.out.println(immutableColors); // Try to modify the immutable colors map immutableColors.put("black", "#000000"); // throws UnsupportedOperationException } } |
Output:
{red=#FF0000, green=#00FF00, blue=#0000FF}
Exception in thread “main” java.lang.UnsupportedOperationException
at com.google.common.collect.ImmutableMap.put(ImmutableMap.java:781)
at Main.main(Main.java:21)
As you can see, the copyOf() method creates a true copy of the original map, and does not allow any modifications to it. This is because the immutable map has its own copy of the data, and does not depend on the original map.
3. Internal working of CopyOf() method
You might wonder how the copyOf() method works internally, and whether it creates a deep or shallow copy of the original map. The answer is that it depends on the type and size of the original map.
- If the original map is already an instance of
ImmutableMap, thecopyOf()method simply returns the same instance, as there is no need to create a new copy. This is an optimization that saves memory and time. - If the original map is not an instance of
ImmutableMap, but is an instance ofSortedMap, thecopyOf()method returns a new instance ofImmutableSortedMap, which preserves the order and comparator of the original map. This is useful if you want to create an immutable sorted map from another sorted map. - If the original map is not an instance of
ImmutableMaporSortedMap, but is a small map with less than five entries, thecopyOf()method returns a new instance ofRegularImmutableMap, which uses a compact data structure that stores the entries in a single array. This is efficient for small maps, as it reduces memory usage and improves cache locality. - If the original map is not an instance of
ImmutableMap,SortedMap, or it is not a small map, thecopyOf()method returns a new instance ofRegularImmutableMap, which uses a hash table data structure that stores the entries in separate arrays for keys and values. This is efficient for large maps, as it provides fast lookups and avoids hash collisions.
In all cases, the copyOf() method creates a shallow copy of the original map, meaning that it copies only the references to the keys and values, not the objects themselves. This means that if the keys or values are mutable objects, any changes to them will be reflected in both maps. For example:
|
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 |
import com.google.common.collect.ImmutableMap; import java.util.*; class Main { public static void main(String[] args) { // Create a mutable list of numbers List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3)); // Create a mutable map that contains the numbers list as a value Map<String, List<Integer>> mutableMap = new HashMap<>(); mutableMap.put("numbers", numbers); // Create an immutable copy of the mutable map ImmutableMap<String, List<Integer>> immutableMap = ImmutableMap.copyOf(mutableMap); // Print both maps System.out.println(mutableMap); // {numbers=[1, 2, 3]} System.out.println(immutableMap); // {numbers=[1, 2, 3]} // Modify the numbers list numbers.add(4); // Print both maps again System.out.println(mutableMap); // {numbers=[1, 2, 3, 4]} System.out.println(immutableMap); // {numbers=[1, 2, 3, 4]} } } |
As you can see, modifying the numbers list affects both maps, even though one of them is immutable. This is because both maps share the same reference to the numbers list, and do not have their own copies of it. Therefore, if you want to create an immutable copy of another map that contains mutable objects as keys or values, you should make sure to create deep copies of those objects before passing them to the copyOf() method.
4. Benefits and drawbacks of using the copyOf() method
Using the copyOf() method of the ImmutableMap class in Guava has some benefits and drawbacks that you should be aware of before using it. Here are some of the benefits:
- It is easy to use, as you only need to pass another map to create an immutable copy of it.
- It is efficient and memory-friendly, as it does not create a copy of the original map if it is already immutable, and uses optimized data structures for different sizes of maps.
- It is consistent and thread-safe, as it preserves the immutability and unmodifiability of the original map.
Here are some of the drawbacks of using the copyOf() method:
- It may be confusing or surprising, as it creates a shallow copy of the original map, meaning that any changes to the mutable keys or values will be reflected in both maps. This can lead to unexpected or inconsistent behavior if the keys or values are not immutable themselves.
- It may be less performant than a mutable map in some cases, such as when creating large maps or iterating over them frequently. This is because the immutable map has to copy the data from the original map, and may use more memory or time than a mutable map.
5. Conclusion
In this post, we have covered how to use the copyOf() method of the ImmutableMap class in Guava to create an immutable map from another map. We have also explained why this method is useful, how it works internally, and what are some of the benefits and drawbacks of using it.
If you want to learn more about this method, you can check out the Guava official website or its GitHub repository.
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 :)