List 與陣列互轉是 Java 開發高頻操作。API 看似簡單,但 toArray() 重載陷阱、Arrays.asList() 固定長度限制、基本類型無法直接轉換等細節極易踩坑。本文系統梳理互轉姿勢、記憶體語義、最佳實踐與避坑清單。
List list = Arrays.asList("A", "B", "C");
Object[] objArr = list.toArray(); // 類型擦除,只能得到 Object[]
// String[] strArr = (String[]) objArr; // ❌ ClassCastException!
缺點: 運行時類型為 Object[],無法安全轉型為具體類型陣列。
List list = Arrays.asList("A", "B", "C");
// 寫法一:傳入「零長度陣列」(JDK 6+ 推薦,JIT 會優化分配精確大小)
String[] arr1 = list.toArray(new String[0]); // ✅ 最簡潔、效能佳
// 寫法二:傳入「預估大小陣列」(舊版 JDK 推薦,避免反射分配)
String[] arr2 = list.toArray(new String[list.size()]); // ✅ 也可
// 寫法三:大小不足時自動擴容(語義同上)
String[] arr3 = list.toArray(new String[10]); // 實際只用前 3 位,第 4 位置 null
null,返回同一陣列引用。Array.newInstance),返回新陣列引用。new T[0]:逃逸分析 + 標量替換可消除分配開銷,比預估大小更快。
String[] arr = {"A", "B", "C"};
List list = Arrays.asList(arr); // 返回 Arrays$ArrayList(非 java.util.ArrayList)
// 特性:
list.get(0); // ✅
list.set(0, "X"); // ✅ 修改元素 → 原陣列同步變化
list.add("D"); // ❌ UnsupportedOperationException(長度固定)
list.remove(0); // ❌ UnsupportedOperationException
String[] arr = {"A", "B"};
List list = Arrays.asList(arr);
list.set(0, "Z");
System.out.println(arr[0]); // "Z" —— 修改 List 影響原陣列
arr[1] = "Y";
System.out.println(list.get(1)); // "Y" —— 修改陣列影響 List
int[] primitiveArr = {1, 2, 3};
// List list = Arrays.asList(primitiveArr); // ❌ 編譯錯誤:泛型不支持基本類型
// Arrays.asList(primitiveArr) 返回 List,長度為 1,元素是原陣列引用
// 正確姿勢:流式轉換(JDK 8+)
List list = Arrays.stream(primitiveArr).boxed().collect(Collectors.toList());
// 或第三方庫
// List list = IntStream.of(primitiveArr).boxed().collect(toList());
// 1. new ArrayList<>(Arrays.asList(...)) —— 經典,複製一份
List mutable1 = new ArrayList<>(Arrays.asList("A", "B"));
// 2. Collections.addAll() —— 效能最佳(避免中間 List 對象)
List mutable2 = new ArrayList<>();
Collections.addAll(mutable2, "A", "B", "C"); // 或 Collections.addAll(mutable2, arr);
// 3. Stream API(JDK 8+)—— 函數式風格
List mutable3 = Stream.of("A", "B", "C").collect(Collectors.toList());
// 並行大數據
List mutable4 = Arrays.stream(arr).parallel().collect(Collectors.toList());
// 二維陣列轉 List
String[][] matrix = {{"A","B"}, {"C","D"}};
List rowList = Arrays.asList(matrix); // List,每行是陣列引用
// rowList.add(new String[]{"E"}); // ❌ 固定長度
// 深度轉換需手動
List> deepList = Arrays.stream(matrix)
.map(row -> new ArrayList<>(Arrays.asList(row)))
.collect(Collectors.toList());
Benchmark Mode Cnt Score Error Units
toArray_newZero thrpt 5 125.3 ± 2.1 ops/ms (new String[0])
toArray_newSize thrpt 5 118.7 ± 1.8 ops/ms (new String[size])
stream_boxed_collect thrpt 5 45.2 ± 0.9 ops/ms (基本類型轉換)
Collections_addAll thrpt 5 210.5 ± 3.4 ops/ms (陣列→可變List最快)
| 陷阱 | 錯誤代碼 | 正確代碼 | 原因 |
|---|---|---|---|
| toArray() 強轉 | (String[]) list.toArray() | list.toArray(new String[0]) | 運行時類型 Object[] |
| asList 添加/刪除 | Arrays.asList(arr).add("X") | new ArrayList<>(Arrays.asList(arr)) | 返回固定長度視圖 |
| 基本類型陣列 asList | Arrays.asList(intArr) | IntStream.of(intArr).boxed().collect(toList()) | 泛型擦除,推導為 List |
| 修改視圖影響原陣列 | list.set(0,"X") 後驚訝 arr 變了 | new ArrayList<>(Arrays.asList(arr)) 斷開共享 | asList 無拷貝,共享底層陣列 |
| 傳入過大陣列污染 null | list.toArray(new String[100]) | list.toArray(new String[0]) | 剩餘位置置 null,可能誤判長度 |
list.toArray(new T[0]) 一把梭(簡潔、高效、類型安全)。new ArrayList<>(Arrays.asList(arr)) 或 Collections.addAll(new ArrayList<>(), arr)。boxed() 或第三方庫轉包裝類型 List。asList 視圖共享底層陣列,new ArrayList 獨立拷貝。Arrays.asList(arr) 或 List.of(...) (JDK 9+),零拷貝、真不可變。掌握這些,List 與陣列互轉不再有坑!