Java Collections.unmodifiableMap 方法
上次修改时间:2025 年 4 月 20 日
Collections.unmodifiableMap
方法是 Java 集合框架的一部分。它返回指定 Map 的不可修改视图。此视图阻止通过返回的引用修改底层 Map。
当您需要提供对 Map 数据的只读访问时,不可修改的 Map 非常有用。它们有助于强制实现不可变性并防止意外修改。如果您保留对原始 Map 的引用,则仍然可以修改它。
基本定义
不可修改的 Map 是现有 Map 的一个包装器,它阻止所有修改操作。尝试修改 Map 将导致 UnsupportedOperationException
异常。此视图是实时的 - 对后备 Map 的更改可以通过不可修改的视图看到。
unmodifiableMap
方法是 java.util.Collections
类中的一个工厂方法。它接受一个 Map 作为参数,并返回该 Map 的不可修改视图。所有 Map 实现都可以通过这种方式进行包装。
创建不可修改的 Map
此示例演示了 Collections.unmodifiableMap
的基本用法。我们创建一个常规的 HashMap,然后获取它的不可修改视图。该示例显示了我们可以从中读取但不能写入不可修改的视图。
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")); } }
此代码显示了不可修改 Map 的基本行为。我们首先创建一个常规的 HashMap 并用一些条目填充它。然后,我们使用 Collections.unmodifiableMap
创建此 Map 的不可修改视图。
该示例演示了虽然我们无法通过不可修改的视图修改 Map,但对原始 Map 的更改会反映在视图中。这表明不可修改的 Map 是一个实时视图,而不是一个独立的副本。
不可修改 Map 操作
此示例探讨了对不可修改 Map 的各种操作。我们将看到允许哪些操作(读取操作)以及哪些操作抛出异常(写入操作)。该示例涵盖了常见的 Map 接口方法。
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"); } } }
此示例演示了各种 Map 操作在不可修改 Map 上的行为。像 size
、containsKey
、get
和 keySet
这样的读取操作可以正常工作。所有修改操作都会抛出 UnsupportedOperationException
。
输出显示,虽然我们可以查询不可修改的 Map,但任何修改它的尝试都会导致异常。这使得不可修改的 Map 成为提供对 Map 数据只读访问的理想选择。
来自 Java 9 Map.of 的不可修改 Map
Java 9 引入了像 Map.of
这样的工厂方法,它们可以直接创建不可变 Map。此示例将其与 Collections.unmodifiableMap
进行比较。工厂方法创建真正的不可变 Map,而不是视图。
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); } }
此示例突出了 Collections.unmodifiableMap
和 Java 9 的 Map.of
之间的区别。虽然两者都阻止通过它们的引用进行修改,但 unmodifiableMap
只是一个潜在可变 Map 的视图,而 Map.of
创建一个完全不可变的 Map。
输出显示,对原始 Map 的更改会反映在不可修改的视图中,而 Java 9 不可变 Map 无法通过任何方式修改。根据您的不可变性要求选择合适的方法。
嵌套的不可修改 Map
此示例演示了创建包含其他不可修改集合的不可修改 Map。我们将看到如何构建复杂的不可变数据结构。该示例显示了一个从国家到其不可修改的城市列表的 Map。
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"); } } }
此示例创建一个深度不可变性结构。我们首先使城市列表不可修改,然后将它们放置在一个 Map 中,最后使 Map 本身不可修改。这确保了结构的任何部分都不能被修改。
该示例显示了尝试修改外部 Map 或嵌套集合都会导致异常。当您需要向应用程序的其他部分提供完全不可变的数据结构时,此方法很有用。
性能注意事项
此示例检查不可修改 Map 的性能特征。我们将比较常规 Map 及其不可修改视图上的操作。测试测量读取操作和内存开销。
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"); } }
此性能测试表明,不可修改的 Map 对于读取操作具有可忽略的开销。get
操作在原始 Map 及其不可修改视图上花费的时间基本相同。
内存测试表明,每个不可修改的包装器会消耗少量额外的内存(通常为 16-32 字节)。对于大多数应用程序而言,这种开销可以忽略不计,这使得不可修改的视图成为提供只读访问的轻量级解决方案。
实际用例
此示例演示了 unmodifiableMap
在配置管理系统中的实际应用。我们将创建一个配置持有者,该持有者提供对应用程序设置的只读访问,同时允许特权代码更新配置。
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); } }
此示例展示了如何在配置管理系统中使用 Collections.unmodifiableMap
。ConfigurationManager
类维护一个可变的内部 Map,但仅向客户端公开一个不可修改的视图。这确保了外部代码无法直接修改配置。
该示例演示了虽然尝试通过不可修改的视图修改配置会失败,但 updateConfiguration
方法仍然可以修改内部 Map,并且这些更改通过视图可见。这种模式在 API 设计中很常见,用于保护内部状态,同时允许受控修改。
来源
Java Collections.unmodifiableMap 文档
在本教程中,我们深入探讨了 Java Collections.unmodifiableMap
方法。我们涵盖了基本用法、Map 操作、与 Java 9 的 Map.of
的比较、嵌套的不可修改结构、性能注意事项以及实际的配置管理用例。此方法对于提供对 Map 数据的只读访问,同时保持根据需要修改底层 Map 的灵活性非常有用。
作者
列出所有Java教程。