Arrays.asList()真的就把数组转换为了List集合吗?
看面试题的时候,里面提到了这个,之前都是常规的使用,把数组转换为List集合,程序每次也都正常的运行,没在意过这个问题。看到这个问题时直接懵了,难道我之前的使用是错误的吗?后来查了资料确实是有点问题,Arrays.asList()还就真就没把数组转换为List集合,源码底层还是一个数组!。
话不多说。直接上代码,结果才是唯一真理。
再简单不过的一个把数组转换为List集合的例子。
1 | public class ArraysTest { |
可以看到程序的第17行报了一个异常,17行就对应着list.add("啥也不会的程序员");
这个方法。出现异常的原因就是调用了add
方法。一开始就说了,底层还是一个数组,而数组的一个重要特点就是,一旦长度确定之后就不可以改变。所以也就导致了,add
方法出现异常。而且不止addss
方法会出现异常,remove
和clear
方法也会出现异常。
现在只是知道了Arrays.asList()
在执行add
等方法的时候会出现异常,但是具体原因是什么还不清楚,而且如果底层是数组的话,怎么又会说把数组转换为List集合呢?
想要知道原理,就要分析源码了。
先看看Arrays.asList()
这个方法的源码是怎么样的
1 | public static <T> List<T> asList(T... a) { |
可以看到asList这个方法中并没有什么特殊的代码,只不过传入了一个可变参数,然后又创建了一个ArrayList
对象并返回。那好,就继续看ArrayList的源码。
1 | private static class ArrayList<E> extends AbstractList<E> |
可以发现,这个类是一个私有的静态内部类。并且有一个带参构造器,构造器需要传入一个泛型数组,而后这个泛型数组在经过非空判断后赋值给了final修饰的泛型数组a。哦,到了这里就会发现,其实本质还是一个数组,一个泛型数组,只不过在这个数组外面套上一个ArrayList
类的外壳。
到了这里就会知道了,其实本质还是一个数组,可是,知道了是数组了,那么异常又是哪里来的呢?平常使用中又是怎么把它伪装成List集合使用的呢?既然不清楚,那就继续看源码。ArrayList
类没有关于异常的源码,那就看它父类AbstractList
的源码。
AbstractList的部分源码
1 | public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { |
可以看到,add
和remove
方法都抛出了UnsupportedOperationException
异常,这里就是关键,在调用add
和remove
方法时,因为是继承了AbstractList
类,而ArrayList
又没有重写add
和remove
方法,则会调用父类的方法,抛出异常。clear
方法和add
不太一样,clear
方法又调用了removeRange
方法,而removeRange
方法中又执行了it.remove();
方法,然后再经过JDK源码的一些执行,最后会执行到remove
方法上,所以也会抛出一个异常。
到此,为什么会抛出异常的原因知道了,但是还是没弄清楚是怎么伪装成List集合使用的。
如果细心就会发现,AbstractList
实现了List
接口,然后基于Java的多态特性,父类引用指向子类对象,自然而然就被当做了List集合使用。
到此,分析过源码之后,就知道了为什么Arrays.asList()
没有把数组转换为List
集合,为什么在调用add
、remove
和clear
方法时会抛出异常。知道了怎么伪装成为List集合使用的。
那么又如何正确的将数组转换为List集合呢?
1、手动实现
1 | private static<T> List<T> arrayToList(T[] array){ |
2、最简单的方法
1 | String[] strings = new String[]{"张三","李四","王二","麻子"}; |
3、Java8的Stream
1 | String[] strings = new String[]{"张三","李四","王二","麻子"}; |