在實(shí)際開(kāi)發(fā)過(guò)程中我們經(jīng)常使用 asList 講數(shù)組轉(zhuǎn)換為 List,這個(gè)方法使用起來(lái)非常方便,但是 asList 方法存在幾個(gè)缺陷:
使用 8 個(gè)基本類(lèi)型數(shù)組轉(zhuǎn)換為列表時(shí)會(huì)存在一個(gè)比較有味的缺陷。先看如下程序:
public static void main(String[] args) {
int[] ints = {1,2,3,4,5};
List list = Arrays.asList(ints);
System.out.println("list'size:" + list.size());
}
------------------------------------
outPut:
list'size:1
程序的運(yùn)行結(jié)果并沒(méi)有像我們預(yù)期的那樣是 5 而是逆天的 1,這是什么情況?先看源碼:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
asList 接受的參數(shù)是一個(gè)泛型的變長(zhǎng)參數(shù),我們知道基本數(shù)據(jù)類(lèi)型是無(wú)法發(fā)型化的,也就是說(shuō) 8 個(gè)基本類(lèi)型是無(wú)法作為 asList 的參數(shù)的, 要想作為泛型參數(shù)就必須使用其所對(duì)應(yīng)的包裝類(lèi)型。但是這個(gè)這個(gè)實(shí)例中為什么沒(méi)有出錯(cuò)呢?因?yàn)樵搶?shí)例是將 int 類(lèi)型的數(shù)組當(dāng)做其參數(shù),而在 Java 中數(shù)組是一個(gè)對(duì)象,它是可以泛型化的。所以該例子是不會(huì)產(chǎn)生錯(cuò)誤的。既然例子是將整個(gè) int 類(lèi)型的數(shù)組當(dāng)做泛型參數(shù),那么經(jīng)過(guò) asList 轉(zhuǎn)換就只有一個(gè) int 的列表了。如下:
public static void main(String[] args) {
int[] ints = {1,2,3,4,5};
List list = Arrays.asList(ints);
System.out.println("list 的類(lèi)型:" + list.get(0).getClass());
System.out.println("list.get(0) == ints:" + list.get(0).equals(ints));
}
--------------------------------------------
outPut:
list 的類(lèi)型:class [I
list.get(0) == ints:true
從這個(gè)運(yùn)行結(jié)果我們可以充分證明 list 里面的元素就是 int 數(shù)組。弄清楚這點(diǎn)了,那么修改方法也就一目了然了:將 int 改變?yōu)?Integer。
public static void main(String[] args) {
Integer[] ints = {1,2,3,4,5};
List list = Arrays.asList(ints);
System.out.println("list'size:" + list.size());
System.out.println("list.get(0) 的類(lèi)型:" + list.get(0).getClass());
System.out.println("list.get(0) == ints[0]:" + list.get(0).equals(ints[0]));
}
----------------------------------------
outPut:
list'size:5
list.get(0) 的類(lèi)型:class java.lang.Integer
list.get(0) == ints[0]:true
Java 細(xì)節(jié)(2.1):在使用 asList 時(shí)不要將基本數(shù)據(jù)類(lèi)型當(dāng)做參數(shù)。
對(duì)于上面的實(shí)例我們?cè)僮鲆粋€(gè)小小的修改:
public static void main(String[] args) {
Integer[] ints = {1,2,3,4,5};
List list = Arrays.asList(ints);
list.add(6);
}
該實(shí)例就是講 ints 通過(guò) asList 轉(zhuǎn)換為 list 類(lèi)別,然后再通過(guò) add 方法加一個(gè)元素,這個(gè)實(shí)例簡(jiǎn)單的不能再簡(jiǎn)單了,但是運(yùn)行結(jié)果呢?打出我們所料:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(Unknown Source)
at java.util.AbstractList.add(Unknown Source)
at com.chenssy.test.arrayList.AsListTest.main(AsListTest.java:10)
運(yùn)行結(jié)果盡然拋出 UnsupportedOperationException 異常,該異常表示 list 不支持 add 方法。這就讓我們郁悶了,list 怎么可能不支持 add 方法呢?難道 JDK 腦袋堵塞了?我們?cè)倏?asList 的源碼:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
asList 接受參數(shù)后,直接 new 一個(gè) ArrayList,到這里看應(yīng)該是沒(méi)有錯(cuò)誤的???別急,再往下看:
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) {
if (array==null)
throw new NullPointerException();
a = array;
}
//.................
}
這是 ArrayList 的源碼,從這里我們可以看出,此 ArrayList 不是 java.util.ArrayList,他是 Arrays 的內(nèi)部類(lèi)。該內(nèi)部類(lèi)提供了 size、toArray、get、set、indexOf、contains 方法,而像 add、remove 等改變 list 結(jié)果的方法從 AbstractList 父類(lèi)繼承過(guò)來(lái),同時(shí)這些方法也比較奇葩,它直接拋出 UnsupportedOperationException 異常:
public boolean add(E e) {
add(size(), e);
return true;
}
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();
}
通過(guò)這些代碼可以看出 asList 返回的列表只不過(guò)是一個(gè)披著 list 的外衣,它并沒(méi)有 list 的基本特性(變長(zhǎng))。該 list 是一個(gè)長(zhǎng)度不可變的列表,傳入?yún)?shù)的數(shù)組有多長(zhǎng),其返回的列表就只能是多長(zhǎng)。所以:
Java 細(xì)節(jié)(2.2):不要試圖改變 asList 返回的列表,否則你會(huì)自食苦果。