bigywater.tech
Java 内部原理 · 26

Arrays.asList() 真实是个固定大小的视图:改它真的会改到原始数组

Arrays.asList(arr) 几乎是每个 Java 开发者都用过的方法,但"它返回的到底是不是一个真正独立的 List"这个问题,真实验证一次比记住结论更靠得住。

view.set() → 原数组跟着变
真实验证:view.set(0, 999) 之后,原始数组 backing 真实也跟着变成了 999,不是各自独立的两份数据
add() → 真实抛异常
真实验证:view.add()/view.remove() 真实抛出 UnsupportedOperationException,因为它是固定大小的视图

上一篇:Arrays.sort 的稳定性:真实验证 Object[] 排序会保留相同分数的原始顺序

真实验证:set() 是双向联动的

Arrays.asList() 返回的是 java.util.Arrays 内部的一个私有 ArrayList 实现,它内部持有的就是传进来的那个数组引用本身,不是拷贝。

真实实测 · view.set() 和直接改数组
view = [1, 2, 3, 4, 5]

view.set(0, 999) 之后:
  view    = [999, 2, 3, 4, 5]
  backing = [999, 2, 3, 4, 5](原始数组是否真的被改了?)

直接改 backing[1] = 888 之后:
  backing = [999, 888, 3, 4, 5]
  view    = [999, 888, 3, 4, 5](view 是否也跟着变了?)

两个方向的真实数据都确认了同一件事:view 和 backing 共享同一份底层存储,改任何一边,另一边都会真实同步反映出来。这和系列前面Collections.unmodifiableList 那篇的"视图不是拷贝"是同一类问题,只是这次视图本身还允许 set。

真实验证:add/remove 真实抛异常

虽然允许 set(替换已有位置的元素),但 Arrays.asList() 返回的是固定大小视图,不允许改变长度。

真实实测
view.add(6) 真实抛出 UnsupportedOperationException
view.remove(0) 真实抛出 UnsupportedOperationException

原因很直接:底层数组的长度在创建时就已经固定,add/remove 需要真实改变元素个数,而数组本身做不到这一点(Java 数组一旦创建长度不可变)——Arrays.asList() 返回的这个内部类没有重写 add/remove,直接继承了 AbstractList 里默认的、真实抛出 UnsupportedOperationException 的实现。

真实验证:想要真正独立的 List,得显式拷贝

如果确实需要一个可以自由增删、又不影响原始数组的 List,标准做法是把 asList 的结果再包一层 ArrayList 构造函数。

真实实测
new ArrayList<>(Arrays.asList(backing)) 得到真正的 ArrayList,add(777) 成功: [999, 888, 3, 4, 5, 777]
对原始 backing 数组真实没有任何影响 = [999, 888, 3, 4, 5]
这次 add 真实成功了,且原始数组真实没有被这次操作影响——因为 ArrayList 的这个构造函数会把传入集合的元素真实拷贝进一个新分配的内部数组
那 Arrays.asList() 到底该在什么场景用?它最适合的场景是"我已经有一个数组,只是想暂时当 List 用一下"(比如传给某个只接受 List 参数的方法、或者用 Collections.sort 排序原数组),这时候视图语义反而是优点,不用额外拷贝。真正会踩坑的是把它的返回值直接存下来、当成一个可以自由增删的普通 ArrayList 长期使用——这个真实的类型差异,编译器不会提醒你,只有运行到 add 那一刻才会真实抛出异常。

分步演示:view 和 backing 是怎么"同步"的

每一步的数组内容都是真实运行这段代码之后的状态,不是手工画的联动关系。

view.set() 改到 backing,backing 改了 view 也变,add/remove 真实抛异常,拷贝之后才真正独立
这一步真实发生变化的位置

    参考与说明

    • 本文全部结果均在本机真实运行 Java 17 得到。
    • 和Collections.unmodifiableList 那篇是"视图 vs 拷贝"这同一个主题的两个例子,建议对照读。
    • 没有涉及:List.of()(Java 9+ 真正不可变的工厂方法,和 Arrays.asList 语义完全不同)——这些留给系列后面按需再写。
    京ICP备2026067502号-1京公网安备11010502063487号