首先Arrays.asList()可不简单,得少用,是这道题让我深入认识这个函数,来看看输出结果?
public static void main(String[] args) {
Integer[] datas = {1,2,3,4,5};
List<Integer> list = Arrays.asList(datas);
list.add(5);
System.out.println(list.size());
}
结果:运行异常,不允许添加元素。要是答错了,下面的知识可得看看了!!程序员的素养课来啦!
Arrays.asList()
作用是将数组转化为集合,但它返回的集合并非如你所愿。先来看看《阿里巴巴Java开发手册》的讲述:
使用工具类
Arrays.asList()
把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear
方法会抛出UnsupportedOperationException
异常。(调用的add/remove其实是AbstractList
的方法,该方法就是抛出异常。后面会讲)说明:
asList
的返回对象是一个Arrays 内部类,并没有实现集合的修改方法。Arrays.asList()
体现的是适配器模式,只是转换接口,后台的数据仍是数组。
在解决以上问题前,先看看Arrays.asList()的使用吧
9.1 基本数据类型数组转化为List
int[] data = new int[]{1,2,3,4,5};
List list = Arrays.asList(data);
System.out.println(list.size());
输出结果是1,也就是说数组转化为List,是将整个数组作为一个元素!看看 Arrays.asList()
的函数如何定义就能理解了:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
可以 asList()
看到使用了可变长泛型来定义,泛型的实例化必须是类,而基本数据类型不支持泛型化。案例中没有规定泛型,于是 asList
将数组作为泛型的实例,解决办法就是使用包装类,如下:
@Test
public void test(){
Integer[] data = new Integer[]{1,2,3,4,5};
List<Integer> list = Arrays.asList(data);
System.out.println(list.size());
}
输出结果:5
9.2 数组转换为集合后进行增删操作
public static void main(String[] args) {
Integer[] datas = {1,2,3,4,5};
List<Integer> list = Arrays.asList(datas);
list.add(5);
System.out.println(list.size());
}//运行结果:抛出异常!
首先 Arrays.asList()
返回的并非是 ArrayList
!自然调用 add()
等方法无法达到预期了。那为什么编译不报错,而是运行报错呢,可见此处调用其他类的方法,而非 ArrayList()
的方法!!
通过调试发现, Arrays.asList()
返回的并非是 ArrayList
!(看红框框处)
![image-20230318131842099](https://typora-2296.oss-cn-shenzhen.aliyuncs.com/202303181318182.png)
其实 Arrays.asList()
返回的是 java.util.Arrays.ArrayList
,这个家伙是谁呢。看源码
public class Arrays {
//省略其他方法
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
//here
private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable{
private final E[] a;
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
@Override
public int size() {
return a.length;
}
}
}
Arrays.ArrayList
是工具类 Arrays 的一个内部静态类,可以看到该内部静态类继承了 AbstractList
,定义ArrayList
类时重写的方法有:(其中set修改操作就是支持的!)
![image-20230523184147916](https://typora-2296.oss-cn-shenzhen.aliyuncs.com/202305231841063.png)
至于其他的增删操作调用的就是父类 AbstractList
中定义的方法了!回到之前的题目,数组转换为集合后调用增删操作,其实调用的就是 AbstractList
中的方法,而 AbstractList
中的方法实际上是实现了List中的方法,就是抛出异常,所以**编译不报错(有方法可调),运行抛异常(方法抛异常)**看源码:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
//略
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
//略
}
而 ArrayList直接实现了List 接口,实现了List所有方法,真正实现了增删等操作。
这里可以看看继承关系图。理清思路,Collection(接口)和List(接口)中定义了增删改查等操作,但没有方法体实现。AbstractList(类)实现了List中方法,也就是抛出异常,ArrayList(类)继承AbstractList(类)并实现List,对增删改查操作真正意义的实现
![image-20230318134608317](https://typora-2296.oss-cn-shenzhen.aliyuncs.com/202303181346445.png)
总结,就是 Arrays.asList()
返回数据类型 Arrays.ArrayList
而非 ArrayList
9.3 数组转化为集合后的数据异常
看看此题输出?
@Test
public void test() {
String[] arr = {"欢迎","关注","Java"};
List list = Arrays.asList(arr);
arr[1] = "爱上";
list.set(2,"我");
System.out.println(Arrays.toString(arr));
System.out.println(list.toString());
}
结果:
![image-20230318135132005](https://typora-2296.oss-cn-shenzhen.aliyuncs.com/202303181351032.png)
啊?都一样,震惊一百年啊哥们?
回到刚才的asList的源码:
//Arrays中asList
public class Arrays {
//省略其他方法
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
//省略其他方法
}
//ArrayList<>(a);
ArrayList(E[] array) {
a = Objects.requireNonNull(array);//这里的a时内部定义的泛型数组
}
//requireNonNull()
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
可以看到这是直接将数组引用返回了啊,所以数组和转化后的集合实际上公用一个数组,本质依旧数组,改了其中一个,数据都会变化。你要是不知道,数据异常的事情你是抠破脑袋也想不明白的呀!
总结asList的坑:
- 基本数据类型数组转化集合只有一个元素(基本数据类型不支持泛型化)
- 数组转化为集合,该集合不能增删改查(抛出异常,不合预期)
- 数据转化为集合,本质依旧是数组,公用一块空间(内部直接引用赋值)
9.4 数组如何真正意义转化为ArrayList集合
方法一:遍历添加(较low)
Integer intArray[] = {1, 2, 3};
ArrayList<Integer> aList = new ArrayList<>();
for (Integer i: intArray){
aList.add(i);
}
方法二:用Collections工具类中方法(addAll后面的参数为可变泛型,也就是不支持基本数据类型)
List<String> list = new ArrayList();
Collections.addAll(list, "welcome", "to", "china");
System.out.println(list);
此法效率高?too young too simple!
addAll()
方法的实现就是用的上面遍历的方式。
方法三:将Arrays.asList返回的集合作为ArrayList的构造参数
ArrayList arrayList = new ArrayList<>(Arrays.asList("welcome", "to", "china"));
没有方法可以直接让基本数据类型的数组转化为集合,只能一个个添加,或者使用包装类。