不可变集合:从防御性编程说起
为什么要不可变集合
不可变集合 = 创造后不能修改的集合。
为什么不用可变集合?防御性编程:防止被意外修改。
java
// ❌ 返回可变集合:调用者可能修改你的数据
public List<String> getColors() {
return new ArrayList<>(Arrays.asList("red", "green", "blue"));
}
// 某处调用:
colors.add("yellow"); // 你的数据被意外修改了!
// ✅ 返回不可变集合:调用者无法修改
public List<String> getColors() {
return List.of("red", "green", "blue");
}JDK 9+ 工厂方法
List.of
java
// 创建不可变列表
List<String> immutable = List.of("a", "b", "c");
// 尝试修改
immutable.add("d"); // UnsupportedOperationException
immutable.set(0, "x"); // UnsupportedOperationException
immutable.remove("a"); // UnsupportedOperationExceptionSet.of
java
Set<Integer> immutable = Set.of(1, 2, 3);
// 不允许重复
Set.of(1, 1, 2); // IllegalArgumentException: 重复元素!Map.of
java
Map<String, Integer> immutable = Map.of(
"a", 1,
"b", 2,
"c", 3
);
// Map.ofEntries - 更多条目(JDK 9+)
Map<String, Integer> big = Map.ofEntries(
Map.entry("one", 1),
Map.entry("two", 2)
);Arrays.asList vs List.of
java
// Arrays.asList:返回固定大小的列表,可修改元素但不能增删
String[] array = {"a", "b", "c"};
List<String> list = Arrays.asList(array);
list.set(0, "x"); // ✅ OK:修改元素
list.add("d"); // ❌ UnsupportedOperationException:不能增删
// List.of:完全不可变
List<String> listOf = List.of("a", "b", "c");
listOf.set(0, "x"); // ❌ UnsupportedOperationException
listOf.add("d"); // ❌ UnsupportedOperationException关键区别:asList 的底层是原数组,List.of 是全新的不可变实现。
asList 的陷阱
java
String[] array = {"a", "b", "c"};
List<String> list = Arrays.asList(array);
list.set(0, "x");
System.out.println(Arrays.toString(array)); // [x, b, c]
// 原数组也被修改了!Collections 工具方法
空集合
java
List<String> empty = Collections.emptyList(); // 不可变
Set<Integer> empty2 = Collections.emptySet();
Map<String, Integer> empty3 = Collections.emptyMap();单元素集合
java
List<String> single = Collections.singletonList("only");
Set<String> singleSet = Collections.singleton("only");
Map<String, Integer> singleMap = Collections.singletonMap("key", 1);unmodifiableXxx:包装为不可变
java
List<String> original = new ArrayList<>(Arrays.asList("a", "b", "c"));
List<String> unmodifiable = Collections.unmodifiableList(original);
// 但如果修改原集合,unmodifiable 也会变化!
original.add("d");
System.out.println(unmodifiable); // [a, b, c, d]不可变集合的典型应用
1. 作为常量配置
java
public class AppConfig {
public static final List<String> SUPPORTED_FORMATS = List.of(
"JSON", "XML", "YAML", "CSV"
);
public static final Map<String, String> DEFAULT_HEADERS = Map.of(
"Content-Type", "application/json",
"Accept", "application/json"
);
}2. 作为方法返回值(保护内部状态)
java
public class UserService {
private final List<User> users = new ArrayList<>();
public List<User> getUsers() {
return List.copyOf(users); // 返回不可变副本
}
}3. 多线程共享
java
// 不可变集合天然线程安全,不需要同步
public static final Set<String> THREAD_SAFE_CODES = Set.of("OK", "ERROR", "PENDING");
// 多个线程可以安全地读取,不需要 synchronizedJDK 17+ 的新选择
java
// Set.copyOf / List.copyOf / Map.copyOf
List<String> mutable = new ArrayList<>();
mutable.add("a");
List<String> immutable = List.copyOf(mutable); // JDK 10+总结
| 要点 | 说明 |
|---|---|
| List.of | JDK 9+,完全不可变,不能增删改 |
| Arrays.asList | 固定大小,可改元素但不能增删 |
| Collections.emptyXxx | 不可变空集合 |
| Collections.singletonXxx | 不可变单元素集合 |
| Collections.unmodifiableXxx | 包装为不可变 |
一句话:不可变集合是「只读护照」——保证你的数据不会被意外修改。
