Arrays.asList()真的就把数组转换为了List集合吗?

看面试题的时候,里面提到了这个,之前都是常规的使用,把数组转换为List集合,程序每次也都正常的运行,没在意过这个问题。看到这个问题时直接懵了,难道我之前的使用是错误的吗?后来查了资料确实是有点问题,Arrays.asList()还就真就没把数组转换为List集合,源码底层还是一个数组!。

话不多说。直接上代码,结果才是唯一真理。

再简单不过的一个把数组转换为List集合的例子。

public class ArraysTest {
    
    
    public static void main(String[] args) {
    
    
        String[] strings = new String[]{
    
    "张三","李四","王二","麻子"};
        List<String> list = Arrays.asList(strings);
        list.add("啥也不会的程序员");
    }
}

在这里插入图片描述

可以看到程序的第17行报了一个异常,17行就对应着list.add("啥也不会的程序员");这个方法。出现异常的原因就是调用了add方法。一开始就说了,底层还是一个数组,而数组的一个重要特点就是,一旦长度确定之后就不可以改变。所以也就导致了,add方法出现异常。而且不止addss方法会出现异常,removeclear方法也会出现异常。

现在只是知道了Arrays.asList()在执行add等方法的时候会出现异常,但是具体原因是什么还不清楚,而且如果底层是数组的话,怎么又会说把数组转换为List集合呢?

想要知道原理,就要分析源码了。

先看看Arrays.asList()这个方法的源码是怎么样的

public static <T> List<T> asList(T... a) {
    
    
    return new ArrayList<>(a);
}

可以看到asList这个方法中并没有什么特殊的代码,只不过传入了一个可变参数,然后又创建了一个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);
        }
    // 后面的省略,不是重点
    }

可以发现,这个类是一个私有的静态内部类。并且有一个带参构造器,构造器需要传入一个泛型数组,而后这个泛型数组在经过非空判断后赋值给了final修饰的泛型数组a。哦,到了这里就会发现,其实本质还是一个数组,一个泛型数组,只不过在这个数组外面套上一个ArrayList类的外壳。

到了这里就会知道了,其实本质还是一个数组,可是,知道了是数组了,那么异常又是哪里来的呢?平常使用中又是怎么把它伪装成List集合使用的呢?既然不清楚,那就继续看源码。ArrayList类没有关于异常的源码,那就看它父类AbstractList的源码。

AbstractList的部分源码

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    
    
    public boolean add(E e) {
    
    
        add(size(), e);
        return true;
    }
    
    public void add(int index, E element) {
    
    
        throw new UnsupportedOperationException();
    }
    public E remove(int index) {
    
    
        throw new UnsupportedOperationException();
    }
    
    public void clear() {
    
    
        removeRange(0, size());
    }
    
    protected void removeRange(int fromIndex, int toIndex) {
    
    
        ListIterator<E> it = listIterator(fromIndex);
        for (int i=0, n=toIndex-fromIndex; i<n; i++) {
    
    
            it.next();
            it.remove();
        }
    }
    // 省略...
}

可以看到,addremove方法都抛出了UnsupportedOperationException异常,这里就是关键,在调用addremove方法时,因为是继承了AbstractList类,而ArrayList又没有重写addremove方法,则会调用父类的方法,抛出异常。clear方法和add不太一样,clear方法又调用了removeRange方法,而removeRange方法中又执行了it.remove();方法,然后再经过JDK源码的一些执行,最后会执行到remove方法上,所以也会抛出一个异常。

到此,为什么会抛出异常的原因知道了,但是还是没弄清楚是怎么伪装成List集合使用的。

如果细心就会发现,AbstractList实现了List接口,然后基于Java的多态特性,父类引用指向子类对象,自然而然就被当做了List集合使用。

到此,分析过源码之后,就知道了为什么Arrays.asList()没有把数组转换为List集合,为什么在调用addremoveclear方法时会抛出异常。知道了怎么伪装成为List集合使用的。

那么又如何正确的将数组转换为List集合呢?

1、手动实现

private static<T> List<T> arrayToList(T[] array){
    
    
        List<T> list = new ArrayList<>();
        for (T t:array){
    
    
            list.add(t);
        }
        return list;
    }

2、最简单的方法

String[] strings = new String[]{
    
    "张三","李四","王二","麻子"};
List<String> list1 = new ArrayList<>(Arrays.asList(strings));

3、Java8的Stream

String[] strings = new String[]{
    
    "张三","李四","王二","麻子"};
List<String> list = Arrays.stream(strings).collect(Collectors.toList());

猜你喜欢

转载自blog.csdn.net/Asdzxc968/article/details/106738779