java不可变集合指由collections.unmodifiablexxx()或jdk 10+ immutablecollections生成、运行时抛unsupportedoperationexception的实例;它非只读视图,也不等价线程安全,仅拦截写操作,底层引用仍可被修改。

不可变集合在Java里到底指什么
Java里的不可变集合(Immutable Collection)不是“不能修改”的接口或抽象概念,而是明确由Collections.unmodifiableXXX()或java.util.ImmutableCollections(JDK 10+ 内部实现)生成的、运行时抛出UnsupportedOperationException的实例。它不等于“只读视图”,更不等于“线程安全”——只是把写操作拦截掉,底层引用仍可能被其他可变集合改写。
怎么创建真正安全的不可变集合
用Collections.unmodifiableList()这类包装器是常见误区:它只保护包装层,原始集合一旦被修改,不可变视图也会变。要真正安全,必须确保源头不可变:
- 优先使用
List.of()、Set.of()、Map.of()(JDK 9+),它们返回的是紧凑、不可变、非空、无null的实例 - 若需含null或动态构建,用
ImmutableList.copyOf()(Guava)或new ArrayList(source).unmodifiable()(JDK 14+Collection.copyOf()) - 避免用
Arrays.asList()再套unmodifiableList():前者返回的仍是可变数组-backed 列表
不可变集合和并发安全的关系
不可变集合天然线程安全,但仅限于“自身状态不被修改”。它不解决以下问题:
- 如果集合元素本身是可变对象(比如
List<person></person>中Person字段可变),整体仍不安全 - 不可变集合不保证构造过程的原子性;多线程同时调用
List.of(a, b, c)没问题,但若a/b/c来自共享可变状态,需额外同步 -
Stream.toList()(JDK 16+)返回的是不可变列表,但前提是stream源本身稳定
遇到UnsupportedOperationException该怎么查
这个异常几乎只说明一点:你误把不可变集合当可变用了。典型场景包括:
立即学习“Java免费学习笔记(深入)”;
- 对
Set.of("a", "b")调用add()或remove() - 从Spring配置或Jackson反序列化得到一个集合,但框架默认返回不可变类型(如
LinkedHashSet被替换为不可变实现) - MyBatis查询结果被自动包装成不可变
List(某些版本或配置下)
排查时直接搜at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add堆栈,定位哪行代码试图修改。
真正麻烦的从来不是“怎么建不可变集合”,而是搞清谁持有它的可变副本、元素是否真不可变、以及上下游框架是否悄悄替换了实现。










