在最近做的一個項目中對List進行操作時報錯java.lang.UnsupportedOperationException,後來發現操作的List是由數組轉換而成的,通過看源碼發現問題,並寫測試程序如下。
代碼塊:
public class Unsupported {
static void test(String msg, List<String> list) {
System.out.println("---- " + msg + "----");
Collection<String> c = list;
Collection<String> subList = list.subList(1, 8);
// 複製子List
Collection<String> c2 = new ArrayList<>(subList);
try {
c.retainAll(c2);
} catch (Exception e) {
System.out.println("retainAll()方法的異常: " + e);
}
try {
c.removeAll(c2);
} catch (Exception e) {
System.out.println("removeAll()方法的異常: " + e);
}
try {
c.clear();
} catch (Exception e) {
System.out.println("clear()方法的異常: " + e);
}
try {
c.add("X");
} catch (Exception e) {
System.out.println("add()方法的異常: " + e);
}
try {
c.addAll(c2);
} catch (Exception e) {
System.out.println("addAll()方法的異常: " + e);
}
try {
c.remove("C");
} catch (Exception e) {
System.out.println("remove()方法的異常: " + e);
}
// list.set() 方法修改值,但是沒有改變數據結構的大小
try {
list.set(0, "X");
} catch (Exception e) {
System.out.println("list.set()方法的異常: " + e);
}
}
public static void main(String[] args) {
List<String> list = Arrays.asList("A B C D E F G H I J K L".split(" "));
test("複製操作: ", new ArrayList<String>(list));
System.out.println("**********一條華麗的分割線**********");
test("Arrays.asList的操作: ", list);
test("unmodifiableList()的操作: ", Collections.unmodifiableList(list));
}
}
執行結果:
---- 複製操作: ----
**********一條華麗的分割線**********
---- Arrays.asList的操作: ----
retainAll()方法的異常: java.lang.UnsupportedOperationException
removeAll()方法的異常: java.lang.UnsupportedOperationException
clear()方法的異常: java.lang.UnsupportedOperationException
add()方法的異常: java.lang.UnsupportedOperationException
addAll()方法的異常: java.lang.UnsupportedOperationException
remove()方法的異常: java.lang.UnsupportedOperationException
---- unmodifiableList()的操作: ----
retainAll()方法的異常: java.lang.UnsupportedOperationException
removeAll()方法的異常: java.lang.UnsupportedOperationException
clear()方法的異常: java.lang.UnsupportedOperationException
add()方法的異常: java.lang.UnsupportedOperationException
addAll()方法的異常: java.lang.UnsupportedOperationException
remove()方法的異常: java.lang.UnsupportedOperationException
list.set()方法的異常: java.lang.UnsupportedOperationException
產生的原因
調用Arrays.asList()生產的List的add、remove方法時報異常,這是由Arrays.asList() 返回的是Arrays的內部類ArrayList, 而不是java.util.ArrayList。Arrays的內部類ArrayList和java.util.ArrayList都是繼承AbstractList,remove、add等方法AbstractList中是默認throw UnsupportedOperationException而且不作任何操作。java.util.ArrayList重新了這些方法而Arrays的內部類ArrayList沒有重新,所以會拋出異常。解決方法如下:
通過再新建一個ArrayList,讓這些方法重新實現AbstractList的方法,如下圖:
以下內容摘自Java編程思想(第四版):
因爲Arrays.asList()會生成一個List,它基於一個固定大小的數組,而這個數組僅支持那些不會改變數組大小的操作。任何會引起對底層數據結構的尺寸進行修改的方法都會產生一個UnsupportedOperation Exception異常,以表示對未獲支持操作的調用(一個編程錯誤)。
注意,應該把Arrays.asList()的結果作爲構造器的參數傳遞給任何Collection(或者使用addAll()方法,或者 Collections.addAll()靜態方法),這樣可以生成允許使用所有的方法的普通容器----這在 main()方法中的第一個對test的調用中得到了展示,這樣的調用會產生新的尺寸可調的底層數據結構。 Collections類中的“不可修改”的方法將容器包裝到了一個代理中,只要你執行任何試圖修改容器的操作,這個代理都會產生UnsupportedOperationException異常。使用這些方法的目標就是產生“常量”容器對象。“不可修改”的Collections方法的完整列表將在稍後介紹。
test方法中的最後一個try語句塊將檢查作爲List的一部分的set()方法。這很有趣,因爲你可以看到“未獲支持的操作”這一技術的粒度來的是多麼方便----所產生的“接口”可以在Arrays. asListCollections()返回的對象和Collections.unmodifiableList()返回的對象之間,在一個方法的粒度上產生變化。 Arrays.asList()返回固定尺寸的List,而 Collections. unmodifiableListO產生不可修改的列表。正如從輸出中所看到的,修改 Arrays. asList()返回的List中的元素是可以的,因爲這沒有違反該List“尺寸固定”這一特性但是很明顯,unmodifiableList()的結果在任何情況下都應該不是可修改的。如果使用的是接口,那麼還需要兩個附加的接口,一個具有可以工作的set()方法,另外一個沒有,因爲附加的接口對於 Collection的各種不可修改的子類型來說是必需的。
【參考資料】
1、Java編程思想(第四版)