关于Arrays.asList返回的List无法新增和删除?

一、问题重现:一行代码引发的异常

日常开发中,我们常通过Arrays.asList()将数组转为List,但调用add/remove时会抛出异常:

Integer[] array = {1, 2, 3, 4, 5};
List<Integer> list = Arrays.asList(array);
list.add(11); // 运行报错:UnsupportedOperationException

这是为什么?同样是List接口,为什么new ArrayList<>()能正常增删,而这里却不行?

二、源码揭秘:两个"ArrayList"的差异

1. 返回的不是普通ArrayList

Arrays.asList()返回的是java.util.Arrays静态内部类ArrayList,而非我们常用的java.util.ArrayList

// Arrays类的静态内部类(简化版)
private static class ArrayList<E> extends AbstractList<E> {
private final E[] a; // 持有原数组引用(final修饰)

ArrayList(E[] array) {
    a = Objects.requireNonNull(array); // 直接引用原数组,不做拷贝
}

// 只实现了get/set/size等读取和修改元素的方法
@Override
public E get(int index) { return a[index]; }
@Override
public E set(int index, E element) { /* 修改元素 */ }
@Override
public int size() { return a.length; }

// 关键:未重写add()和remove()方法!

}

2. 为什么不能新增/删除?

Arrays内部类ArrayList继承了AbstractList,但没有实现add/remove等修改集合结构的方法。而AbstractList的默认实现就是直接抛异常:

public abstract class AbstractList<E> {
// 未被重写时,调用add就会抛异常
public void add(int index, E element) {
    throw new UnsupportedOperationException();
}

// remove同理
public E remove(int index) {
    throw new UnsupportedOperationException();
}

}
对比之下,我们常用的java.util.ArrayList重写了这些方法,通过动态扩容(拷贝新数组) 实现元素的新增和删除,因此支持修改操作。

三、解决方案:获取可修改的List

如果需要对转换后的List进行增删操作,推荐两种方案:

方案1:用ArrayList包装(最常用)

通过java.util.ArrayList的构造方法,将Arrays.asList()的结果传入。这个过程会拷贝原数组元素,生成独立的可变List:

Integer[] array = {1, 2, 3};
// 包装后得到可修改的List
List<Integer> mutableList = new ArrayList<>(Arrays.asList(array));
mutableList.add(4); // 正常执行,无异常

方案2:Stream流转换(指定具体实现类)

Java 8+的Stream流可以转换集合,但需注意:Collectors.toList()在不同JDK版本中返回的实现类不同(可能是可变或不可变)。若要确保可修改,建议显式指定ArrayList

// 显式指定用ArrayList接收,确保可修改
List<Integer> mutableList = Arrays.stream(array)
    .collect(Collectors.toCollection(ArrayList::new));

四、扩展:Stream流转换的List也可能不可修改

很多人以为Stream流转换的List一定可以修改,这其实是个误区。Collectors.toList()的返回值在JDK 8中是ArrayList(可变),但在JDK 9及以上版本中,可能返回不可修改的集合(如java.util.ImmutableCollections$ListN):

// JDK 11环境下可能出现的情况
List<Integer> list = Arrays.stream(array).collect(Collectors.toList());
list.add(11); // 可能抛UnsupportedOperationException

原因:JDK 9+为了优化性能,默认返回不可变集合(节省内存、线程安全)。若需确保可修改,必须像方案2那样显式指定ArrayList作为容器

五、注意事项

  1. 原数组与List的关联性
    Arrays.asList()返回的List直接引用原数组,修改原数组会同步影响List:

    array[0] = 100;
    System.out.println(list.get(0)); // 输出100(而非原1)
    
  2. 避免直接使用不可变List
    若仅需遍历或修改元素(set),Arrays.asList()完全可用;若需增删元素,必须通过上述方案转换为可变List。

总结

  1. Arrays.asList()返回的是Arrays内部类ArrayList,未实现add/remove,因此不可增删;
  2. 转换为可修改List的可靠方式:new ArrayList<>(Arrays.asList(array))Stream流指定ArrayList
  3. JDK 9+中Collectors.toList()可能返回不可变集合,需显式指定实现类。
posted @ 2024-10-01 07:41  Liberty码农志  阅读(100)  评论(0)    收藏  举报