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
Map countryCities = 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教程。