asList 引发的血案
程序员文章站
2022-06-08 08:24:46
...
详情见如下代码:
@Test
public void testArraysList(){
Integer[] a = {1,2,3,4,5};
List<Integer> list = Arrays.asList(a);
//尝试着向集合中再添加一个元素。
list.add(6);
for( Integer temp : list ) {
System.out.println(temp);
}
}
很简单的程序啊,不过,这段程序执行会不会有什么问题呢?
运行后,会出现如下结果:
java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
UnsupportedOperationException,不支持的操作。居然不支持 List 的 add 方法,真是奇怪了!还是追根寻源,看看 asList 的源码:
@SuppressWarnings("varargs")
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
直接 new 了一个 ArrayList ,难道 ArrayList 不支持 add 方法?不可能呀!问题就出在这个 ArrayList 上,此处的 ArrayList 并非我们熟悉的 java.util.ArrayList,而是 ArrayList 工具类中的一个内置类。
//这是一个私有的静态内部类
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);
}
/*其他方法省略*/
}
这里的 ArrayList 是一个静态私有内部类,除了 Arrays 能访问外,其他类度不能访问。仔细看这个类,发现它没有提供 add 方法,那肯定是父类 AbstractList 提供的,来看代码:
public boolean add(E e) {
add(size(), e);
return true;
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
父类确实提了 add 方法,但是没有具体的实现,所以每个子类都要覆写 add 方法,而 ArrayList 没有覆写,因此 add 一个元素就会报错。
通过源码发现,ArrayList 静态内部类,它实现了如下一些常用方法:
- size:元素数量
- toArray:转换为一个数组
- get:获得指定元素
- set:重置指定元素
- indexOf:返回指定元素的索引下标
- contains:是否包含某元素
-
sort:排序
对于我们经常使用的 List.add 和 List.remove() 方法,都没有实现,也就是说, asList 返回的是一个长度不可变的列表,数组是多长,转换的列表也就是多长,不再保持列表动态边长的特性。
虽然 Arrays.asList 用起便捷,但是却深藏着重大隐患 —— 列表长度无法修改,切记千万不要把这样的 List 对象传递到一个允许 add 操作的方法中。