Skip to content

不可变集合:从防御性编程说起

为什么要不可变集合

不可变集合 = 创造后不能修改的集合。

为什么不用可变集合?防御性编程:防止被意外修改。

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"); // UnsupportedOperationException

Set.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");

// 多个线程可以安全地读取,不需要 synchronized

JDK 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.ofJDK 9+,完全不可变,不能增删改
Arrays.asList固定大小,可改元素但不能增删
Collections.emptyXxx不可变空集合
Collections.singletonXxx不可变单元素集合
Collections.unmodifiableXxx包装为不可变

一句话:不可变集合是「只读护照」——保证你的数据不会被意外修改。


相关链接

基于 VitePress 构建