1. 概述
在 Java 开发中,我们经常需要将数组转换为 List
。两种常见写法是:
Arrays.asList(array)
new ArrayList<>(Arrays.asList(array))
看起来差不多,但行为差异巨大。踩过坑的人都知道,一不留神就会遇到 UnsupportedOperationException
或莫名其妙的数据共享问题。本文就来彻底讲清楚它们的区别。
2. Arrays.asList:返回的是一个“伪 List”
Arrays.asList()
看似返回一个 List
,实则是个“包装视图”(wrapper view),它并没有拷贝数组内容,而是直接把原数组当作 List
来暴露。
✅ 特点总结:
- 返回的
List
是固定长度的 - ❌ 不支持
add()
、remove()
等改变结构的操作 - ✅ 可以通过
set(index, element)
修改元素 - ⚠️ 所有修改都会直接反映到原始数组上
示例代码
String[] stringArray = new String[] { "A", "B", "C", "D" };
List<String> stringList = Arrays.asList(stringArray);
修改 List
中的元素:
stringList.set(0, "E");
assertThat(stringList).containsExactly("E", "B", "C", "D");
assertThat(stringArray).containsExactly("E", "B", "C", "D"); // 原数组也被改了!
尝试添加元素:
stringList.add("F"); // ❌ 直接抛异常
抛出异常:
java.lang.UnsupportedOperationException
at java.base/java.util.AbstractList.add(AbstractList.java:153)
背后原理
你可能会奇怪:Arrays.asList()
返回的不是 java.util.ArrayList
吗?
错!它返回的是 Arrays
类内部的一个私有静态类 ArrayList
—— 和 java.util.ArrayList
完全不是同一个东西!
// 在 java.util.Arrays 中
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private final E[] a; // 直接引用传入的数组
...
}
这个内部类继承了 AbstractList
,但没有重写 add()
和 remove()
方法,因此调用时会抛出 UnsupportedOperationException
。
⚠️ 所以记住:Arrays.asList()
返回的 List
是“只读长度”的,适合做参数传递或临时使用,但不适合后续修改。
3. new ArrayList<>(Arrays.asList(array)):真正的独立副本
如果你需要一个可自由增删改的 ArrayList
,正确的做法是:
String[] stringArray = new String[] { "A", "B", "C", "D" };
List<String> stringList = new ArrayList<>(Arrays.asList(stringArray));
这行代码做了两件事:
Arrays.asList(array)
:将数组转为固定长度的List
视图new ArrayList<>(...)
:以该视图为参数,构造一个新的ArrayList
,复制所有元素
✅ 结果特性:
- 是一个标准的
java.util.ArrayList
- ✅ 支持
add()
、remove()
、clear()
等操作 - ✅ 修改
List
不会影响原始数组 - ✅ 原始数组修改也不会影响
List
(因为是值拷贝)
验证不影响原数组
stringList.set(0, "E");
assertThat(stringList).containsExactly("E", "B", "C", "D");
assertThat(stringArray).containsExactly("A", "B", "C", "D"); // 原数组没变 ✅
添加元素测试
stringList.add("F"); // ✅ 成功添加
assertThat(stringList).containsExactly("A", "B", "C", "D", "F");
没有异常,操作成功。
4. 总结对比
特性 | Arrays.asList(arr) |
new ArrayList<>(Arrays.asList(arr)) |
---|---|---|
是否可 add() / remove() |
❌ 抛 UnsupportedOperationException |
✅ 支持 |
是否独立副本 | ❌ 共享原数组 | ✅ 独立拷贝 |
修改元素是否影响原数组 | ✅ 影响 | ❌ 不影响 |
内部实现 | Arrays.ArrayList (静态内部类) |
java.util.ArrayList |
适用场景 | 临时使用、只读操作 | 需要后续增删改的场景 |
简单粗暴记忆法
- 想要“能改长度的 List” → 用
new ArrayList<>(Arrays.asList(arr))
- 只是“换个方式读数组” → 用
Arrays.asList(arr)
小贴士
如果数组是基本类型(如 int[]
),Arrays.asList()
会把整个数组当成一个对象处理,返回 List<int[]>
,这通常不是你想要的。这种情况建议使用 Stream
:
int[] ints = {1, 2, 3};
List<Integer> list = Arrays.stream(ints).boxed().collect(Collectors.toList());
5. 结语
别再混淆了!Arrays.asList()
返回的是一个“受限的视图”,而 new ArrayList<>(...)
才是真正生成一个可变的副本。搞不清这点,轻则运行时报错,重则数据被意外修改。
代码示例已上传至 GitHub:https://github.com/baeldung/core-java-collections-conversions