Collections.nCopies返回的是不可变视图,本质是共享同一对象引用的假复制,所有元素指向同一实例,修改任一元素会影响全部,且所有修改操作均抛UnsupportedOperationException。

用 Collections.nCopies 初始化固定大小集合,本质是「假复制」
Collections.nCopies 返回的不是真副本,而是一个共享同一对象引用的视图。它节省内存,但所有元素指向同一个实例——改其中一个,全跟着变。
- 常见错误现象:
List<string> list = Collections.nCopies(3, new String("a")); list.set(0, "b");</string>看似安全,但若传入的是可变对象(比如new ArrayList()),后续修改该对象会影响整个列表 - 使用场景:适合初始化含不可变值(如
"hello"、42、Boolean.TRUE)的只读占位列表,比如测试填充、UI占位数据 - 参数差异:
nCopies(int n, T o)中n必须 ≥ 0;传负数直接抛IllegalArgumentException - 性能影响:O(1) 创建,不分配 n 个对象;但每次
get(i)都返回同一引用,无额外开销
为什么 Collections.nCopies 返回的是不可变列表
它返回的是 java.util.Collections$CopiesList,内部继承自 AbstractList,但重写了所有修改方法(add、set、remove 等)并统一抛 UnsupportedOperationException。
- 错误信息:
java.lang.UnsupportedOperationException—— 不是运行时逻辑错,而是设计上就禁止修改 - 兼容性影响:JDK 1.2 起就有,所有版本行为一致;但和
Arrays.asList类似,表面像List,实则功能受限 - 注意点:不能直接强转成
ArrayList或调用stream().toList()(JDK 16+)来“绕过”,因为底层类不支持Serializable的完整契约,某些序列化框架会出问题
想初始化可变的固定大小集合,得绕开 nCopies
如果需要一个真正独立、可修改的固定长度列表,nCopies 就不合适了——必须显式构造新对象。
- 推荐做法:
Arrays.asList(new String[5])或Stream.generate(() -> new String("x")).limit(5).collect(Collectors.toList()),但更稳妥的是用循环或ArrayList构造器 +addAll - 简洁写法(JDK 11+):
List<string> list = Stream.generate(() -> "default").limit(5).collect(Collectors.toCollection(ArrayList::new));</string> - 容易踩的坑:
Arrays.asList(new String[5])返回的仍是不可变列表(Arrays$ArrayList),只是允许set,不支持add/remove;真要自由增删,必须包一层new ArrayList(...) - 性能提示:生成 10k 个新对象时,
Stream.generate比传统 for 循环略慢 10–15%,但代码清晰度高;高频路径建议用循环
unmodifiableList 和 nCopies 的关系别搞混
Collections.nCopies 返回的列表自带不可变性,不需要再套一层 Collections.unmodifiableList —— 多套一层只会多一层代理,还可能掩盖真实类型信息。
立即学习“Java免费学习笔记(深入)”;
- 错误操作:
Collections.unmodifiableList(Collections.nCopies(3, "a")),编译通过但纯属冗余 - 正确判断依据:看返回类型是否为
java.util.Collections$CopiesList,它是 JDK 内部实现,不对外暴露,也不应被 instanceof 检查 - 调试技巧:打印
list.getClass()可看到类似class java.util.Collections$CopiesList,这是它不可变的最直接证据 - 复杂点在于:这种不可变是「深度」的——你甚至不能用反射去改它的内部数组,因为根本没存数组,只是靠计算索引返回同一个对象









