Java Collections.unmodifiableMap Method
Last modified: April 20, 2025
The Collections.unmodifiableMap
method is part of Java's Collections
Framework. It returns an unmodifiable view of the specified map. This view
prevents modifications to the underlying map through the returned reference.
Unmodifiable maps are useful when you need to provide read-only access to map data. They help enforce immutability and prevent accidental modifications. The original map can still be modified if you maintain a reference to it.
Basic Definitions
An unmodifiable map is a wrapper around an existing map that blocks all
modification operations. Attempts to modify the map result in
UnsupportedOperationException
. The view is live - changes to the
backing map are visible through the unmodifiable view.
The unmodifiableMap
method is a factory method in the
java.util.Collections
class. It takes a Map as parameter and
returns an unmodifiable view of that map. All map implementations can be
wrapped this way.
Creating an Unmodifiable Map
This example demonstrates the basic usage of Collections.unmodifiableMap
.
We create a regular HashMap, then obtain an unmodifiable view of it. The example
shows that we can read from but not write to the unmodifiable view.
package com.zetcode; import java.util.Collections; import java.util.HashMap; import java.util.Map; public class UnmodifiableMapBasic { public static void main(String[] args) { Map<String, Integer> scores = new HashMap<>(); scores.put("Alice", 85); scores.put("Bob", 92); scores.put("Charlie", 78); // Create unmodifiable view Map<String, Integer> unmodifiableScores = Collections.unmodifiableMap(scores); // Can read from unmodifiable map System.out.println("Bob's score: " + unmodifiableScores.get("Bob")); try { // Attempt to modify unmodifiable map unmodifiableScores.put("Diana", 88); } catch (UnsupportedOperationException e) { System.out.println("Cannot modify unmodifiable map: " + e.getMessage()); } // Original map can still be modified scores.put("Diana", 88); System.out.println("Diana's score (via unmodifiable view): " + unmodifiableScores.get("Diana")); } }
This code shows the basic behavior of an unmodifiable map. We first create a
regular HashMap and populate it with some entries. Then we create an unmodifiable
view of this map using Collections.unmodifiableMap
.
The example demonstrates that while we can't modify the map through the unmodifiable view, changes to the original map are reflected in the view. This shows that the unmodifiable map is a live view, not an independent copy.
Unmodifiable Map Operations
This example explores various operations on an unmodifiable map. We'll see which operations are allowed (read operations) and which throw exceptions (write operations). The example covers common Map interface methods.
package com.zetcode; import java.util.Collections; import java.util.HashMap; import java.util.Map; public class UnmodifiableMapOperations { public static void main(String[] args) { Map<String, String> countries = new HashMap<>(); countries.put("USA", "Washington"); countries.put("France", "Paris"); countries.put("Japan", "Tokyo"); Map<String, String> unmodifiableCountries = Collections.unmodifiableMap(countries); // Allowed operations System.out.println("Size: " + unmodifiableCountries.size()); System.out.println("Contains France? " + unmodifiableCountries.containsKey("France")); System.out.println("Capital of Japan: " + unmodifiableCountries.get("Japan")); System.out.println("All countries: " + unmodifiableCountries.keySet()); // Disallowed operations (all throw UnsupportedOperationException) try { unmodifiableCountries.put("Germany", "Berlin"); } catch (UnsupportedOperationException e) { System.out.println("\nCannot add to unmodifiable map"); } try { unmodifiableCountries.remove("USA"); } catch (UnsupportedOperationException e) { System.out.println("Cannot remove from unmodifiable map"); } try { unmodifiableCountries.clear(); } catch (UnsupportedOperationException e) { System.out.println("Cannot clear unmodifiable map"); } } }
This example demonstrates the behavior of various Map operations on an
unmodifiable map. Read operations like size
,
containsKey
, get
, and keySet
work normally. All modification operations throw
UnsupportedOperationException
.
The output shows that while we can query the unmodifiable map, any attempt to modify it results in an exception. This makes unmodifiable maps ideal for providing read-only access to map data.
Unmodifiable Map from Java 9 Map.of
Java 9 introduced factory methods like Map.of
that create
immutable maps directly. This example compares these with
Collections.unmodifiableMap
. The factory methods create truly
immutable maps rather than views.
package com.zetcode; import java.util.Collections; import java.util.HashMap; import java.util.Map; public class UnmodifiableMapJava9 { public static void main(String[] args) { // Java 8 and earlier approach Map<String, Integer> ages = new HashMap<>(); ages.put("John", 30); ages.put("Mary", 25); Map<String, Integer> unmodifiableAges = Collections.unmodifiableMap(ages); // Java 9+ approach Map<String, Integer> immutableAges = Map.of("John", 30, "Mary", 25); System.out.println("Unmodifiable map: " + unmodifiableAges); System.out.println("Immutable map: " + immutableAges); // Both throw UnsupportedOperationException when modified try { unmodifiableAges.put("Alice", 28); } catch (UnsupportedOperationException e) { System.out.println("\nCannot modify unmodifiable map"); } try { immutableAges.put("Alice", 28); } catch (UnsupportedOperationException e) { System.out.println("Cannot modify immutable map"); } // Difference: original can still modify unmodifiable view ages.put("Alice", 28); System.out.println("\nAfter original modification: " + unmodifiableAges); } }
This example highlights the difference between Collections.unmodifiableMap
and Java 9's Map.of
. While both prevent modification through their
references, unmodifiableMap
is just a view of a potentially mutable
map, whereas Map.of
creates a completely immutable map.
The output shows that changes to the original map are reflected in the unmodifiable view, while the Java 9 immutable map cannot be modified by any means. Choose the appropriate approach based on your immutability requirements.
Nested Unmodifiable Maps
This example demonstrates creating an unmodifiable map containing other unmodifiable collections. We'll see how to build complex immutable data structures. The example shows a map of countries to their unmodifiable lists of cities.
package com.zetcode; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; public class NestedUnmodifiableMap { public static void main(String[] args) { // Create mutable lists of cities List<String> usCities = new ArrayList<>(); usCities.add("New York"); usCities.add("Los Angeles"); usCities.add("Chicago"); List<String> frCities = new ArrayList<>(); frCities.add("Paris"); frCities.add("Lyon"); frCities.add("Marseille"); // Create unmodifiable lists List<String> unmodifiableUsCities = Collections.unmodifiableList(usCities); List<String> unmodifiableFrCities = Collections.unmodifiableList(frCities); // Create map of countries to their city lists MapcountryCities = new HashMap<>(); countryCities.put("USA", unmodifiableUsCities); countryCities.put("France", unmodifiableFrCities); // Create unmodifiable view of the map Map unmodifiableCountryCities = Collections.unmodifiableMap(countryCities); System.out.println("Country cities: " + unmodifiableCountryCities); // Attempting to modify at any level will throw exceptions try { unmodifiableCountryCities.get("USA").add("Houston"); } catch (UnsupportedOperationException e) { System.out.println("\nCannot modify cities list"); } try { unmodifiableCountryCities.put("Germany", Collections.unmodifiableList(new ArrayList<>(List.of("Berlin")))); } catch (UnsupportedOperationException e) { System.out.println("Cannot modify countries map"); } } }
This example creates a deep immutability structure. We first make the city lists unmodifiable, then place them in a map, and finally make the map itself unmodifiable. This ensures no part of the structure can be modified.
The example shows that attempts to modify either the outer map or the nested collections all result in exceptions. This approach is useful when you need to provide completely immutable data structures to other parts of your application.
Performance Considerations
This example examines the performance characteristics of unmodifiable maps. We'll compare operations on regular maps versus their unmodifiable views. The test measures read operations and memory overhead.
package com.zetcode; import java.util.Collections; import java.util.HashMap; import java.util.Map; public class UnmodifiableMapPerformance { public static void main(String[] args) { final int SIZE = 1_000_000; final int ITERATIONS = 100; // Create large map Map<Integer, String> bigMap = new HashMap<>(); for (int i = 0; i < SIZE; i++) { bigMap.put(i, "Value" + i); } // Create unmodifiable view Map<Integer, String> unmodifiableBigMap = Collections.unmodifiableMap(bigMap); // Measure get operation performance long start = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { bigMap.get(i % SIZE); } long duration = System.currentTimeMillis() - start; System.out.println("Regular map get: " + duration + " ms"); start = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { unmodifiableBigMap.get(i % SIZE); } duration = System.currentTimeMillis() - start; System.out.println("Unmodifiable map get: " + duration + " ms"); // Measure memory usage Runtime runtime = Runtime.getRuntime(); runtime.gc(); long memoryBefore = runtime.totalMemory() - runtime.freeMemory(); Map<Integer, String>[] maps = new Map[1000]; for (int i = 0; i < 1000; i++) { maps[i] = Collections.unmodifiableMap(new HashMap<>()); } runtime.gc(); long memoryAfter = runtime.totalMemory() - runtime.freeMemory(); System.out.println("Memory per unmodifiable map wrapper: " + (memoryAfter - memoryBefore) / 1000 + " bytes"); } }
This performance test demonstrates that unmodifiable maps have negligible
overhead for read operations. The get
operation takes essentially
the same time on both the original map and its unmodifiable view.
The memory test shows that each unmodifiable wrapper consumes a small amount of additional memory (typically 16-32 bytes). This overhead is insignificant for most applications, making unmodifiable views a lightweight solution for providing read-only access.
Real-world Use Case
This example demonstrates a practical application of unmodifiableMap
in a configuration management system. We'll create a configuration holder that
provides read-only access to application settings while allowing privileged code
to update the configuration.
package com.zetcode; import java.util.Collections; import java.util.HashMap; import java.util.Map; public class ConfigurationManager { private Map<String, String> configMap = new HashMap<>(); private Map<String, String> unmodifiableConfigView = Collections.unmodifiableMap(configMap); public ConfigurationManager() { // Initialize with default values configMap.put("timeout", "30"); configMap.put("max_connections", "10"); configMap.put("debug_mode", "false"); } // Public method to get read-only configuration public Map<String, String> getConfiguration() { return unmodifiableConfigView; } // Privileged method to update configuration public void updateConfiguration(String key, String value) { configMap.put(key, value); } public static void main(String[] args) { ConfigurationManager configManager = new ConfigurationManager(); // Get read-only configuration Map<String, String> config = configManager.getConfiguration(); System.out.println("Initial configuration: " + config); // Try to modify through unmodifiable view try { config.put("timeout", "60"); } catch (UnsupportedOperationException e) { System.out.println("\nCannot modify configuration through view"); } // Update through privileged method configManager.updateConfiguration("timeout", "60"); System.out.println("Updated configuration: " + config); } }
This example shows how to use Collections.unmodifiableMap
in a
configuration management system. The ConfigurationManager
class
maintains a mutable internal map but exposes only an unmodifiable view to
clients. This ensures that external code cannot modify the configuration
directly.
The example demonstrates that while attempts to modify the configuration through
the unmodifiable view fail, the updateConfiguration
method can
still modify the internal map, and those changes are visible through the view.
This pattern is common in API design to protect internal state while allowing
controlled modifications.
Source
Java Collections.unmodifiableMap Documentation
In this tutorial, we've explored the Java Collections.unmodifiableMap
method in depth. We've covered basic usage, map operations, comparisons with
Java 9's Map.of
, nested unmodifiable structures, performance
considerations, and a practical configuration management use case. This method
is valuable for providing read-only access to map data while maintaining
flexibility to modify the underlying map when needed.
Author
List all Java tutorials.