跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

怎么利用 Collections.unmodifiableList 创建只读的列表视图

Collections.unmodifiableList返回的是原列表的只读代理视图而非新列表,读操作委托原列表,写操作抛UnsupportedOperationException;它不复制数据,故原列表修改会实时反映在视图中。 为什么
Collections.unmodifiableList
返回的不是新列表而是视图
Collections.unmodifiableList
不复制元素,只包装原
List
对象,返回一个代理对象。所有读操作(如
get()
size()
)委托给底层列表,所有写操作(如
add()
set()
clear()
)直接抛出
UnsupportedOperationException
。 这意味着:如果原始列表后续被修改,不可修改视图会立刻反映这些变化;反之,试图通过视图修改会立即失败。 适合场景:需要临时暴露一个列表供外部只读访问,且你仍控制原始列表生命周期 不适合场景:想彻底冻结数据快照(此时应先
new ArrayList(original)
再包装) 注意:它不阻止对列表中可变对象的内部修改(例如列表里是
StringBuilder
,仍可调用其
append()
) 如何避免“假只读”:原始列表泄露问题 常见错误是把私有字段直接传给
unmodifiableList
后返回,但调用方可能偷偷保留原始引用:
private List data = new ArrayList<>(); public List getData() { return Collections.unmodifiableList(data); // ❌ data 仍可从别处被修改 }
正确做法是确保原始列表不被外部持有: 构造时用防御性拷贝:
new ArrayList(source)
再包装 或在类初始化后不再暴露原始引用(例如用
final
字段 + 构造器注入,且不提供 setter) 若原始列表来自外部输入,务必先拷贝:
Collections.unmodifiableList(new ArrayList(input))
unmodifiableList
List.of()
的关键区别 JDK 9+ 提供的
List.of()
创建的是真正不可变(immutable)实例,底层无修改入口、不可继承、序列化安全;而
unmodifiableList
只是运行时拦截写操作的包装器。
List.of("a", "b")
:空实现、无 backing list、
size()
等直接返回常量,性能略优
Collections.unmodifiableList(new ArrayList<>(Arrays.asList("a","b")))
:有实际 backing list,内存占用稍高,且若原始
ArrayList
被反射篡改(极端情况),视图可能异常 兼容性:
List.of()
不支持
null
元素;
unmodifiableList
继承原始列表的
null
容忍性 嵌套结构下只读不等于深层只读 如果你的列表里存的是自定义对象(比如
User
),
unmodifiableList
只锁住列表结构,不影响
User
实例自身的状态变更:
List users = new ArrayList<>(); users.add(new User("Alice")); List roUsers = Collections.unmodifiableList(users); roUsers.get(0).setName("Bob"); // ✅ 合法!User 对象本身仍可变
要真正“冻结”内容,需配合不可变类型(如使用
record
、或手动实现不可变
User
类),或在封装层做深拷贝 ——
unmodifiableList
本身不处理这一层。 最容易被忽略的一点:只读列表的“只读性”完全依赖于你是否切断了所有通往原始列表的引用路径,以及其中元素是否自身可变。这两层都得自己兜底。

相关文章