collections.unmodifiablelist仅提供只读外壳,不复制数据,底层修改会反映到视图中;真正不可变应选list.copyof(jdk10+)或guava的immutablelist.copyof。

直接用 Collections.unmodifiableList 不能真正防修改
它只是套了一层“只读外壳”,底层原集合一旦被改,不可变视图会立刻反映变化。这不是 bug,是设计使然——它不复制数据,只拦截写操作。
常见错误现象:UnsupportedOperationException 只在你调用 add、remove、set 等写方法时抛出;但如果你保留了原始 ArrayList 引用并修改它,unmodifiableList 返回的对象内容就悄悄变了。
- 使用场景:适合临时封装传参,防止下游代码误调写方法
- 不适合场景:需要长期持有、跨线程共享、或要求底层数据绝对隔离时
- 性能影响:零拷贝,开销极小;但安全代价是“假不可变”
Java 10+ 推荐用 List.copyOf 替代
List.copyOf 才是真·不可变:它会创建新数组,且返回的是 ImmutableCollections.ListN(JDK 内部实现),连反射都改不了。
注意:参数不能为 null,且要求输入 List 不含 null 元素(否则抛 NullPointerException)。
立即学习“Java免费学习笔记(深入)”;
- 如果源列表可能为
null,先判空:list == null ? List.of() : List.copyOf(list) - 如果源列表含
null,得手动过滤或转成ArrayList后再用unmodifiableList(但依然不彻底) - 兼容性:仅 JDK 10+,老项目升级前需确认基线版本
Guava 的 ImmutableList.copyOf 更灵活
它允许 null 元素,也接受 null 输入(返回空不可变列表),且在 JDK 8/9 下也能用。
不过要注意:它仍会遍历原集合,如果原集合本身是懒加载或有副作用(比如数据库游标),copyOf 会触发全部计算。
- 推荐写法:
ImmutableList.copyOf(list),比手写new ImmutableList.Builder().addAll(list).build()简洁 - 和 JDK
List.copyOf不同,它不要求元素非空,但也不做深拷贝——对象引用照旧 - 若源集合可能被并发修改,必须在调用前加锁或确保已不可变
别忘了数组和 Set、Map 的对应方案
Collections.unmodifiableList 只管 List,同系列还有 unmodifiableSet、unmodifiableMap,但它们都有同样问题:外壳只读,底子能动。
真正可靠的替代是:
- 数组 →
Arrays.asList(arr).toArray()再用List.copyOf,或直接Stream.of(arr).toList()(JDK 16+) -
Set→Set.copyOf(set)(JDK 10+),或ImmutableSet.copyOf(set)(Guava) -
Map→Map.copyOf(map)(JDK 10+),注意它不支持nullkey/value;Guava 的ImmutableMap.copyOf支持nullvalue(但不推荐)
最常被忽略的点:不可变不是递归的。即使你把一个 ArrayList<person></person> 转成不可变,Person 对象本身的字段仍可修改——真要锁死,得靠对象设计(比如 Person 字段全 final + 无 setter)。










