collections.ncopies返回不可变视图,无独立存储,调用add/remove/set抛unsupportedoperationexception;需可变时须显式转arraylist。

为什么 Collections.nCopies 生成的列表不能直接 add/remove
它返回的是不可变视图,底层没有独立存储结构,只是对单个元素的“逻辑重复”。调用 add、remove、set 等会修改内容的方法,一律抛出 UnsupportedOperationException。
常见错误现象:
- 写完
List<string> list = Collections.nCopies(3, "a"); list.add("b");</string>,运行时报错UnsupportedOperationException - 误以为它是“快捷构造器”,想拿来当可变列表用
正确做法是:需要可变时,必须显式拷贝
List<String> mutable = new ArrayList<>(Collections.nCopies(3, "a"));
Collections.nCopies 的内存和性能表现
它几乎不占额外空间——内部只存一份元素引用 + 一个整数长度,无论复制 10 次还是 10 万次,对象数量不变。这对初始化大量默认值(比如初始化 1000 个 null 或 false)很友好。
但要注意副作用:
- 如果传入的是可变对象(如
new ArrayList()),所有位置都指向同一个实例,改其中一个,全部跟着变 - 传基本类型包装类(
Integer、Boolean)或字符串是安全的,因为它们不可变
错误示例:
List<List<Integer>> lists = Collections.nCopies(3, new ArrayList<>());<br>lists.get(0).add(1); // lists.get(1) 和 get(2) 也会看到这个 1
替代方案:什么时候不该用 nCopies
当你需要的是「每个元素独立可变」,或者要频繁修改某几项,nCopies 就不是起点,而是隐患源头。
适用场景很窄:仅限于「初始化后只读」或「只读访问 + 后续整体替换」。
- 需要后续逐个
set?用Arrays.asList配合new ArrayList构造 - 要填不同值(比如递增数字)?直接用循环或
Stream.iterate(Java 9+) - 在 Kotlin/Scala 等语言里,类似需求通常用
listOf或fill,语义更明确,也不易误用
和 Arrays.fill、Lists.newArrayList(Guava)的区别
Arrays.fill 作用于数组,且要求数组已存在;nCopies 直接返回 List,无需预分配。
Guava 的 Lists.newArrayList 是可变的,但没提供内置重复构造;它的 Lists.newArrayListWithCapacity 只是预留空间,不填值。
关键差异点:
-
nCopies返回的是RandomAccess列表,支持 O(1) 索引访问,但不支持修改 - Guava 的
ImmutableList.of或ImmutableList.copyOf更接近语义,且明确传达“不可变”意图 - Java 10+ 的
List.of也是不可变,但它不支持重复构造(List.of("a", "a", "a")得手写)
真正省事又安全的写法,其实是:
Arrays.stream(new String[5]).map(i -> "x").toList(); // Java 16+——它可读、可扩展、不共享引用,只是略重一点。
最常被忽略的一点:很多人把 nCopies 当成“构造工具”,其实它本质是个轻量视图工厂。用之前,先问自己一句:这个列表之后真的一次都不改吗?










