首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Arrays.asList() 陷阱大揭秘:為什麼 add/remove 拋 UnsupportedOperationException 及正確轉換姿勢

     編輯:Java 綜合教學

現象重現

String[] arr = {"a", "b", "c"};
List list = Arrays.asList(arr);
list.add("d");  // 拋出 java.lang.UnsupportedOperationException
list.remove(0); // 拋出 java.lang.UnsupportedOperationException

根因深度解析

返回類型不是 java.util.ArrayList

Arrays.asList() 返回 **`java.util.Arrays$ArrayList`**(內部私有靜態類),**不是** `java.util.ArrayList`。

繼承關係與方法實現

AbstractList (java.util)
  ├── ArrayList (java.util)           // 重寫了 add/remove/set 等修改方法
  └── Arrays$ArrayList (java.util.Arrays)  // **未重寫**修改方法,繼承 AbstractList 默認拋異常

AbstractList 默認實現:

public boolean add(E e) { throw new UnsupportedOperationException(); }
public E remove(int index) { throw new UnsupportedOperationException(); }
public E set(int index, E element) { throw new UnsupportedOperationException(); }

Arrays$ArrayList 核心源碼(JDK 17)

private static class ArrayList extends AbstractList
        implements RandomAccess, java.io.Serializable {
    final E[] a;
    ArrayList(E[] array) { a = Objects.requireNonNull(array); }
    public int size() { return a.length; }
    public Object[] toArray() { return a.clone(); }
    public E get(int index) { return a[index]; }
    public E set(int index, E element) {  // 僅實現了 set
        E old = a[index];
        a[index] = element;
        return old;
    }
    // **無 add/remove/clear 等擴容方法** —— 因為底層數組長度固定
}

設計意圖:固定大小的列表視圖

  • **橋接模式**:`Arrays.asList()` 將**固定長度數組**包裝為 **List 介面**,提供統一訪問,不支援結構性修改(增/刪)。
  • **支援 `set(index, val)`**:修改元素值不改變長度,故實現了 `set`。
  • **序列化、RandomAccess** 標記均保留。

正確轉換姿勢:三種方案

方案一:new ArrayList<>(list) —— 最常用、最簡潔

List mutableList = new ArrayList<>(Arrays.asList(arr));
// 或 Java 9+ List.of + new ArrayList<>
List mutableList = new ArrayList<>(List.of("a", "b", "c"));
  • 複製元素到新 ArrayList,擁有獨立底層數組,支援所有修改操作。
  • 時間 O(N)、空間 O(N)。

方案二:Collections.addAll() —— 避免中間 List 對象

List list = Arrays.stream(arr).collect(Collectors.toList());
// 或
List list = Stream.of(arr).collect(Collectors.toCollection(ArrayList::new));

進階坑點:修改原數組影響列表,反之亦然

 list = Arrays.asList(arr);
arr[0] = "z";
System.out.println(list.get(0)); // 輸出 "z" —— **共享底層數組引用**

list.set(1, "y");
System.out.println(arr[1]);      // 輸出 "y" —— 雙向影響

對策:若需完全隔離,**必須拷貝**(方案一/二/三均會拷貝)。

性能對比基準(JMH 參考)

方案耗時 (ns/op)分配率推薦場景
new ArrayList<>(Arrays.asList(arr))~1202 個對象通用首選
Collections.addAll(new ArrayList<>(len), arr)~1001 個對象高頻熱點路徑
Stream.of(arr).collect(toList())~1803+ 對象函數式管道中

最佳實踐清單

  1. **永遠不要**直接對 `Arrays.asList()` 結果調用 `add/remove/clear`。
  2. **需要可變列表**時,立即包裹:`new ArrayList<>(Arrays.asList(...))`。
  3. **僅讀場景**(遍歷、查找、流式處理)可直接用 `Arrays.asList()`,零拷貝、高性能。
  4. **基本類型數組**(`int[]` 等)無法用 `Arrays.asList()`(會產生 `List`),改用 `Arrays.stream(arr).boxed().collect(...)` 或第三方庫。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved