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));

这行代码做了两件事:

  1. Arrays.asList(array):将数组转为固定长度的 List 视图
  2. 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


原始标题:Arrays.asList vs new ArrayList(Arrays.asList()) | Baeldung