【JAVA】谈谈Arrays.asList()

我们在日常的开发中,常常使用到Arrays.asList()这个方法,它能够很轻易地将一个数组转化为一个List。

例如:

        Integer[] a = new Integer[]{1, 2, 3};
        List<Integer> list = Arrays.asList(a);

现在已经是一个List了,我想对这个List做一些基本操作。

        list.add(4);

运行这段语句后,居然出现了java.lang.UnsupportedOperationException,什么情况,不支持这个新增操作吗?进到源码里,一探究竟。

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

不就是返回了一个ArrayList吗?ArrayList什么时候不支持add方法了,再点进这个“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”不是我们经常用到的那个ArrayList,这个只是Arrays的一个内部类而已。此外,这个“ArrayList”继承AbstractList,却并没有重写它的set、add、remove方法,AbstractList源码如下所示。

    public E set(int index, E element) {
        throw new UnsupportedOperationException();
    }

    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }

    public E remove(int index) {
        throw new UnsupportedOperationException();
    }

还有一个很重要的一点就是,这个“ArrayList”的构造方法,对传进来的数组没有做任何处理,仅仅是直接使用。那么,我们改变原数组中某个索引下标的元素,“ArrayList”也会做同样的改变。

好,既然这个“ArrayList”仅仅支持集合常用的get方法,那现在看这个例子,这一次我使用基本数据类型的数组。

        int[] a = new int[]{1, 2, 3};
        List list = Arrays.asList(a);
        System.out.println(list.get(0));
        System.out.println(list.get(1));

直接运行后,就报出了如下的错误

[I@1b6d3586
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1
    at java.util.Arrays$ArrayList.get(Arrays.java:3841)
    at com.yang.testList.Main.main(Main.java:12)

看的出来,这个“ArrayList”第一个元素输出是一个地址,尝试获取第二个元素时,直接就越界了。

原来啊,这个Arrays.asList()方法是一个泛型方法,需要的是一个对象类型的数组,而不是一个基本数据类型的数组。现在我们直接传入基本数据类型的数组,这个“ArrayList”的构造方法无法将基本类型的数组传到参数E[] a中,只能将一整个数组当作E[] a的第一个元素,因此get(0)时,获取到了这个基本数据类型数组的地址首值,而get(1)时,直接产生了越界异常。


看来,Arrays.asList坑挺多啊,那么,有什么其他方法能够将数组转为List呢?

(1)将Arrays.asList()构造出来的“ArrayList”传入java.util.ArrayList的构造方法里。

        Integer[] a = new Integer[]{1, 2, 3};
        List list = Arrays.asList(a);
        ArrayList arrayList = new ArrayList<>(list);

这个构造方法使用 Arrays.copyOf方法,因此java.util.ArrayList内部的数组和传入的数组将不会有任何的关系。关于数组复制效率的问题,可以参考我的另外一篇文章【JAVA】数组复制效率的比较

(2)使用stream

        Integer[] a = new Integer[]{1, 2, 3};
        List<Integer> collect = Arrays.stream(a).collect(Collectors.toList());

基本数据类型,可以先转化为Stream,再进行装箱即可。

        int[] b = new int[]{1, 2, 3};
        List<Integer> list = Arrays.stream(b).boxed().collect(Collectors.toList());

不过,使用stream进行转换,可能涉及效率问题,这篇文章暂不考虑。

(3)使用Guava

如果说,需要一个不可变集合,可以使用ImmutableList.copyOf方法

        Integer[] a = new Integer[]{1, 2, 3};
        List<Integer> list = ImmutableList.copyOf(a);

可变集合,可以使用Lists.newArrayList方法

        Integer[] a = new Integer[]{1, 2, 3};
        List<Integer> list = Lists.newArrayList(a);
发布了177 篇原创文章 · 获赞 583 · 访问量 115万+

猜你喜欢

转载自blog.csdn.net/qq_33591903/article/details/105193099