Arrays.asList 为什么不能 add/remove
核心原因:它们返回的根本不是 java.util.ArrayList,而是各自内部实现的另一种 List,这些实现故意不支持结构性修改。下面分别说。
一、Arrays.asList 为什么不能 add/remove
Arrays.asList(1, 2, 3) 返回的是 java.util.Arrays 内部的一个静态类 Arrays.ArrayList(注意:不是 java.util.ArrayList,只是名字撞了)。
它的实现大致是:
private static class ArrayList<E> extends AbstractList<E> {
private final E[] a; // 直接持有原始数组的引用
ArrayList(E[] array) {
a = array;
}
public E get(int index) { return a[index]; }
public E set(int index, E element) { ... } // 有 set
public int size() { return a.length; }
// 没有实现 add / remove,继承自 AbstractList 的版本直接抛异常
}
关键点:
- 它直接引用原数组,
a.length就是列表大小。 - 如果允许
add,数组长度是固定的,没法扩容;如果允许remove,删掉后数组里的元素没法“消失”,大小也对不上。 - 所以它只支持改元素(
set,因为数组对应位置可以覆盖),不支持改长度(add/remove)。
调用 add 时会走到 AbstractList.add 的默认实现:
public boolean add(E e) {
add(size(), e);
return true;
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
直接抛 UnsupportedOperationException。
一句话:
Arrays.asList是数组的视图,长度由数组决定,改不了长度,所以不能 add/remove。set是可以的。
二、List.of 为什么完全不能改
List.of(...) 返回的是 java.util.ImmutableCollections 里的 ListN / List12 等内部类,是 Java 9 专门为不可变集合设计的。
它的设计目标就是彻底不可变:
- 不能
add/remove(结构性修改); - 不能
set(连元素都不能换); - 不能包含
null; - 内部字段用
final修饰,元素存在final数组里。
一旦创建,内容就固定。调用任何修改方法,直接抛 UnsupportedOperationException。
三、为什么要这么设计?
1. Arrays.asList 是“视图”,不是独立容器
它和原数组共享数据:
Integer[] arr = {1, 2, 3};
List<Integer> list = Arrays.asList(arr);
list.set(0, 100);
System.out.println(arr[0]); // 100,原数组也变了
如果允许 add / remove,就破坏了“列表长度 = 数组长度”这个前提,语义会混乱。所以只能禁止。
2. List.of 为了不可变性的好处
不可变集合有很多优势:
- 线程安全:多线程读不需要同步。
- 可安全共享:可以当常量反复使用。
- 可作 Map 的 key:因为
hashCode永远不变。 - 防御性拷贝:把不可变集合传出去,不怕被改。
- 性能好:不用考虑扩容、并发修改检查。
所以 List.of 干脆把所有修改方法都禁掉。
四、对比总结
| 写法 | 返回类型 | add/remove | set | 能否含 null |
|---|---|---|---|---|
new ArrayList<>(...) |
java.util.ArrayList |
✅ | ✅ | ✅ |
Arrays.asList(...) |
Arrays$ArrayList |
❌ | ✅ | ✅ |
List.of(...) |
ImmutableCollections$ListN |
❌ | ❌ | ❌ |
五、想要可修改怎么办
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
list.add(4); // ✅
或:
List<Integer> list = new ArrayList<>(List.of(1, 2, 3));
list.add(4); // ✅
new ArrayList<>(...) 会复制一份元素到真正的 ArrayList,之后就能自由增删了。
六、一个小陷阱
List<Integer> list = Arrays.asList(1, 2, 3);
list.add(4); // ❌ UnsupportedOperationException
但很多人以为 Arrays.asList 返回的是 ArrayList,看到类型是 List 就以为能 add,运行时才炸。记住:**Arrays.asList 和 List.of 都是只读或固定长度的,要改就套一层 new ArrayList<>。

浙公网安备 33010602011771号