collections.fill() 用同一对象引用覆盖列表所有元素,导致共享引用和原始对象被回收;仅适用于不可变对象,可变对象需用循环或stream创建独立实例。

Java 中 Collections.fill() 本身不会“导致原始对象引用丢失”,真正的问题在于:它用同一个对象实例反复覆盖 List 的每个位置,使所有元素指向同一引用。这看似是“覆盖”,实则是共享引用引发的副作用——修改任一元素,其他位置同步变化,且原始独立对象因不再被引用而可能被 GC 回收。
fill() 的本质是引用复制,不是深拷贝
Collections.fill(list, obj) 遍历 List,对每个索引执行 list.set(i, obj)。如果传入的是一个已存在的对象(比如 new ArrayList() 或自定义对象),那整个 List 所有元素都保存的是这个对象的同一份引用,而非各自独立的副本。
- 原 List 中各位置曾持有的不同对象引用,会被统一替换为
obj的引用 - 那些被替换掉的原始对象,若再无其他强引用指向它们,就符合垃圾回收条件
- 后续通过 list.get(0) 修改该对象状态,list.get(1) 也会看到相同变化
典型误用场景:想初始化多个独立对象
常见错误写法:
List<list>> matrix = new ArrayList(5); for (int i = 0; i ()); Collections.fill(matrix, new ArrayList()); // ❌ 全部指向同一个空 ArrayList</list>
结果是 matrix 中 5 个元素都引用同一个 ArrayList 实例。向 matrix.get(0).add(1) 后,matrix.get(2).size() 也变成 1。
正确做法应使用循环或 Stream 生成独立实例:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
matrix.replaceAll(unused -> new ArrayList())- 或
matrix = Stream.generate(ArrayList::new).limit(5).collect(toList())
如何判断是否发生了意外的引用共享
运行时可通过以下方式验证:
- 打印各元素的
System.identityHashCode(obj),若全部相等,说明是同一对象 - 修改第一个元素内容后,检查第二个元素是否同步变化
- 使用 IDE 调试器观察 list 内部 elementData 数组,所有 slot 是否指向 heap 中同一地址
注意:obj.equals(another) 为 true 不代表引用相同;必须用 == 或 System.identityHashCode() 判断。
fill() 的安全使用前提
只有在明确需要“所有位置共享同一不可变对象”时,fill() 才是安全的:
- 填充
null、字符串字面量(如 "N/A")、Integer 常量(-128~127 缓存值)等不可变对象 - 填充自定义不可变类(final 字段 + 无 setter)的同一实例
- 确认业务逻辑允许所有元素状态完全同步
一旦涉及可变对象,且需各元素独立状态,fill() 就不适用,必须显式创建新实例。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










