天天看點

【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));
           

直接運作後,就報出了如下的錯誤

[[email protected]

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);