在日常开发中,相信大家或多或少都有用到过一个类 Arrays ,这个类具备的功能还是比较实用的,比如用它做数组的排序,直接 Arrays.sort(数组名) 就搞定,又比如用它将一个数组转换成一个 ArrayList 列表,使用 Arrays.asList(数组名) 就能完成,用起来,哎,挺香的!
美好的事物大家都喜欢,但意外总是存在的,指不定哪天就到来破坏了这份美好,这不,最近我在用 Arrays.asList() 这个方法处理数组时就翻了车,就差找个墙角裂缝钻进去,躲起来了。
有点扯远了,回归正题,结论先行,这里先说结论:利用 Arrays.asList() 方法生成的 ArrayList 列表不能添加或者删除元素。
我反手就是一问,为什么呀?别急,下面一起来剖析一下原因。
🔴问题复现
直接用 IDEA 建一个测试类 TestArrayList.java ,然后写上 main 方法,内容如下:
package com.javabasic.practice;
import java.util.Arrays;
import java.util.List;
/**
* @Description 测试通过Arrays.asList()生成的ArrayList列表不能添加、移除列表元素的原因
* @ClassName TestArrayList
* @Author yuhuofei
* @Date 2023/3/26 16:02
* @Version 1.0
*/
public class TestArrayList {
public static void main(String[] args) {
String[] strings = {"苹果", "桃子", "橙子", "青枣"};
List<String> list = Arrays.asList(strings);
System.out.println("当前的list列表是:" + list);
}
}
运行结果如下图所示,可以看到,这时是能正常完成一个由数组转换成列表的过程的。
添加元素
我们尝试往上面生成的列表里面添加新的元素,比如添加一个“火龙果”,代码如下:
package com.javabasic.practice;
import java.util.Arrays;
import java.util.List;
/**
* @Description 测试通过Arrays.asList()生成的ArrayList列表不能添加、移除列表元素的原因
* @ClassName TestArrayList
* @Author yuhuofei
* @Date 2023/3/26 16:02
* @Version 1.0
*/
public class TestArrayList {
public static void main(String[] args) {
String[] strings = {"苹果", "桃子", "橙子", "青枣"};
List<String> list = Arrays.asList(strings);
System.out.println("当前的list列表是:" + list);
//尝试往list列表中添加一个元素"火龙果"
list.add("火龙果");
System.out.println("添加元素后的list列表是:" + list);
}
}
好家伙,这次直接报错了,莫名其妙的,不信你看看图片里的内容。
删除元素
既然添加元素报错,那我们就再试试删除元素又是什么情况,代码稍微改一下:
package com.javabasic.practice;
import java.util.Arrays;
import java.util.List;
/**
* @Description 测试通过Arrays.asList()生成的ArrayList列表不能添加、移除列表元素的原因
* @ClassName TestArrayList
* @Author yuhuofei
* @Date 2023/3/26 16:02
* @Version 1.0
*/
public class TestArrayList {
public static void main(String[] args) {
String[] strings = {"苹果", "桃子", "橙子", "青枣"};
List<String> list = Arrays.asList(strings);
System.out.println("当前的list列表是:" + list);
//尝试删除list列表中的第一个元素
list.remove(0);
System.out.println("删除元素后的list列表是:" + list);
}
}
同样地,我们运行起来,跑一下看看结果,发现一样地,它也报错了。
🔴原因剖析
这个时候,你是不是脑海里一堆为什么为什么在环绕,甚至还有点意外?要搞清楚上面的问题,那我们得关注 Arrays 这个类的源码实现了。
在上面的代码中,我们是通过 List< String > list = Arrays.asList(strings); 这行代码生成列表的,按住 Ctrl 键,点击鼠标左键,通过 asList 方法进到 Arrays.java 这个类。
为了方便查看,这里把上图相关的源码内容复制出来,其实主要就两个东西,一个是 asList 方法,另一个就是 ArrayList 这个静态内部类,它归属于 Arrays 类。
@SafeVarargs
@SuppressWarnings("varargs")
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
/**
* @serial include
*/
private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
@Override
public int size() {
return a.length;
}
@Override
public Object[] toArray() {
return a.clone();
}
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
int size = size();
if (a.length < size)
return Arrays.copyOf(this.a, size,
(Class<? extends T[]>) a.getClass());
System.arraycopy(this.a, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
@Override
public E get(int index) {
return a[index];
}
@Override
public E set(int index, E element) {
E oldValue = a[index];
a[index] = element;
return oldValue;
}
@Override
public int indexOf(Object o) {
E[] a = this.a;
if (o == null) {
for (int i = 0; i < a.length; i++)
if (a[i] == null)
return i;
} else {
for (int i = 0; i < a.length; i++)
if (o.equals(a[i]))
return i;
}
return -1;
}
@Override
public boolean contains(Object o) {
return indexOf(o) != -1;
}
@Override
public Spliterator<E> spliterator() {
return Spliterators.spliterator(a, Spliterator.ORDERED);
}
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
for (E e : a) {
action.accept(e);
}
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
E[] a = this.a;
for (int i = 0; i < a.length; i++) {
a[i] = operator.apply(a[i]);
}
}
@Override
public void sort(Comparator<? super E> c) {
Arrays.sort(a, c);
}
}
我们来分析一下上面的源码:
-
当外部调用 Arrays.asList() 方法时,其实就是调用了下面的这个方法,而下面这个方法的实现又调用 new ArrayList<>(a) 这个构造方法
public static <T> List<T> asList(T... a) { return new ArrayList<>(a); }
-
new ArrayList<>(a) 这个构造方法属于谁?它是属于 private static class ArrayList 这个静态内部类的,由下面的这行代码可知,这个静态内部类,继承了 AbstractList 这个抽象类,并且还实现了两个接口
private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable
-
具体来看下面的这两行代码,第一行里面有一个 final 关键字,这说明这个数组的长度一旦确定就是不可变的,而第二行则是 private static class ArrayList 这个静态内部类的有参构造方法,也是通过 Arrays.asList() 方法创建 ArrayList 列表的关键点所在,有它存在才有列表产生
private final E[] a; ArrayList(E[] array) { a = Objects.requireNonNull(array); }
-
仔细查看 private static class ArrayList 这个静态内部类,我们会发现,其实它里面并没有 add 和 remove 方法
到这里,我们大致能明白,前面通过 List< String > list = Arrays.asList(strings); 这行代码生成的 ArrayList 列表对象其实是用 Arrays 这个类里面的 private static class ArrayList 这个静态内部类创建的,这个静态内部类它本身并没有 add 和 remove 方法。
那么问题来了,为什么前面的代码能调用 add 和 remove 方法?那是因为继承了一个抽象类 AbstractList ,这个抽象类里面有 add 和 remove 方法,不过只是声明,没有具体的实现逻辑,内容如下:
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*
* <p>This implementation always throws an
* {@code UnsupportedOperationException}.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
throw new UnsupportedOperationException();
}
由代码可知,前面的报错 UnsupportedOperationException ,也是由这里抛出的,至此这一切就都明朗了。而我们用 public class ArrayList 这个类 new 出来的对象,能进行列表元素的添加和删除,其实就在于这个类它自己实现了 add 和 remove 方法,不信你去看源码,肯定是这样。
🔴小结
利用 Arrays.asList() 方法生成的 ArrayList 列表不能添加或者删除元素的原因:
- 有 final 关键字修饰底层数组,数组长度一旦确定不可变
- 最关键也是最根本的原因是,Arrays 里面的 ArrayList 这个静态内部类,本身没有实现 add 和 remove 方法
如果还是想要实现添加或者删除元素,那该怎么办?别慌,改一行代码就可以搞定了。
List<String> list = new ArrayList<>(Arrays.asList(strings));