Collections.nCopies() 返回不可变列表,调用 set/add/remove 均抛 UnsupportedOperationException;需可变副本时应包装为 new ArrayList(Collections.nCopies(n, obj))。

为什么 Collections.nCopies() 返回的列表不能直接修改
它返回的是一个不可变视图,底层共享同一份数据引用。调用 set() 看似成功,但实际会抛出 UnsupportedOperationException;试图 add() 或 remove() 更是直接失败。
常见错误现象:Exception in thread "main" java.lang.UnsupportedOperationException,尤其在你后续想“先复制再改”的时候猝不及防。
- 它不是新分配内存的副本,而是轻量级代理——省内存,但牺牲可变性
- 适用于只读场景:参数默认值、测试桩数据、枚举集合初始化
- 若需后续修改,必须转成可变实现,比如
new ArrayList(Collections.nCopies(n, obj))
如何安全地创建可修改的重复元素列表
最稳妥的做法是把 Collections.nCopies() 的结果包进一个新的 ArrayList —— 这会触发逐个拷贝,生成独立、可变的实例。
List<String> list = new ArrayList<>(Collections.nCopies(3, "hello"));
注意别写成 new ArrayList().addAll(Collections.nCopies(...)),那会多一次循环且语义不清。
- 构造器传入
Collection是标准且高效的方式 - 如果元素是可变对象(如
new HashMap()),注意所有位置引用的是同一个实例——要深拷贝得自己处理 - 对基本类型包装类(
Integer,String)没问题,它们是不可变的
nCopies() 在 Stream 和函数式场景中的误用
有人想用它配合 stream().map() 生成带索引或变化内容的列表,比如“重复 5 次,每次值加 1”——这不行。nCopies() 只能塞完全相同的引用,不支持延迟计算或差异化初始化。
- 需要差异化内容,请用
IntStream.range(0, n).mapToObj(i -> "item-" + i).toList()(Java 16+) -
nCopies()的泛型推导有时会失败,显式指定类型更稳:Collections.<string>nCopies(4, "x")</string> - 性能上它 O(1) 创建,但转成
ArrayList后是 O(n),大数据量时留意初始化开销
替代方案对比:什么时候不该用 nCopies()
如果你需要线程安全、去重、排序、或频繁增删,它就不是合适起点。它的定位非常窄:快速构建小规模、静态、只读的重复列表。
- 要并发读写?用
Collections.synchronizedList(new ArrayList())配合手动填充 - 要避免重复引用导致的副作用?别用
nCopies()塞可变对象,改用循环add(new Xxx()) - 构建常量列表(如配置项白名单)?考虑
List.of()(Java 9+)更语义明确
真正容易被忽略的是:它返回的列表虽然“固定大小”,但大小本身不可变,内容也不可变——这两层不可变性经常被当成“只是不能 add/remove”,结果在 set 上栽跟头。

















