我們在日常的開發中,常常使用到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);