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

Java 陷阱:Arrays.asList() 返回的並非 java.util.ArrayList!

     編輯:Java 相關知識

一個經典的運行時異常

List<String> words = Arrays.asList("i", "love", "you");
words.remove("love"); // 拋出 java.lang.UnsupportedOperationException

許多開發者直覺以為 Arrays.asList() 返回標準的 java.util.ArrayList,支持增刪改查。實際運行卻拋出異常,原因在於它返回的是 java.util.Arrays 內部的一個 私有靜態類 ArrayList,而非 java.util.ArrayList。

源碼級剖析:兩個「ArrayList」的本質區別

1. java.util.Arrays.ArrayList(內部類)

// JDK 源碼簡化版
private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable {
    private final E[] a; // 底層引用外部傳入的數組,final 不可變

    ArrayList(E[] array) { a = Objects.requireNonNull(array); }

    @Override public int size() { return a.length; }
    @Override public E get(int index) { return a[index]; }
    @Override public E set(int index, E element) { // 僅支持替換
        E old = a[index];
        a[index] = element;
        return old;
    }
    // **關鍵點:沒有重寫 remove/add 方法,繼承自 AbstractList**
}

2. AbstractList 的默認實現

public E remove(int index) {
    throw new UnsupportedOperationException(); // 直接拋異常
}
public boolean add(E e) {
    add(size(), e); // 拋異常
}
public void add(int index, E element) {
    throw new UnsupportedOperationException();
}

因此,Arrays.ArrayList 具有 固定長度 特性:支持 get、set、contains、indexOf 等只讀操作,但 不支持任何結構性修改(add/remove/clear)。

3. java.util.ArrayList(標準實現)

  • 底層為動態數組 transient Object[] elementData。
  • 實現完整 List 接口,支持擴容、插入、刪除。
  • 非線程安全,迭代器為 fail-fast。

為什麼設計成這樣?

Arrays.asList(T... a) 的初衷是 「將數組快速包裝為 List 視圖」,以便使用集合框架的豐富 API(如流式操作、遍歷、查找),而非創建獨立可變副本。這種「視圖模式」避免了數組拷貝,性能極高,但也意味著:

  • 底層數組引用共享,外部修改數組內容會反映到 List,反之亦然。
  • 長度不可變,因為數組長度在創建時已固定。

正確的使用姿勢

場景 1:僅需只讀遍歷、查找、流式處理

// 直接使用,零拷貝,高性能
List<String> words = Arrays.asList("apple", "banana", "orange");
words.stream().filter(s -> s.startsWith("a")).forEach(System.out::println);

場景 2:需要增刪改的可變列表

// 方案 A:手動拷貝到新 ArrayList(推薦,語義清晰)
List<String> mutableList = new ArrayList<>(Arrays.asList("a", "b", "c"));
mutableList.add("d"); // OK

// 方案 B:Java 9+ List.of() + new ArrayList()(不可變列表轉可變)
List<String> list = new ArrayList<>(List.of("a", "b", "c"));

// 方案 C:Guava Lists.newArrayList()(第三方庫)
// List<String> list = Lists.newArrayList("a", "b", "c");

場景 3:單元測試或臨時數據構造

// Java 9+ 便利工廠方法(返回不可變列表)
List<String> immutable = List.of("a", "b", "c"); // 不可變,線程安全
// List<String> mutable = new ArrayList<>(List.of("a", "b", "c")); // 需可變時再拷貝

避坑檢查清單

操作Arrays.asList() 返回列表new ArrayList<>(...)
get / set / contains / indexOf✅ 支持✅ 支持
add / remove / clear❌ 拋異常✅ 支持
修改元素內容 (set)✅ 影響原數組✅ 僅影響列表
序列化✅ 支持✅ 支持
線程安全❌ 非線程安全❌ 非線程安全

總結

Arrays.asList() 返回的是 java.util.Arrays.ArrayList —— 一個 固定長度、底層共享數組、不支持結構修改 的 List 視圖。它不是 java.util.ArrayList。遇到 UnsupportedOperationException 時,請檢查是否誤將其當作可變列表使用。需要增刪改時,請顯式拷貝 new ArrayList<>(Arrays.asList(...)) 或使用 Java 9+ List.of() + 拷貝模式。理解「視圖」與「副本」的區別,是避開此坑的關鍵。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved