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

Java 數組與 List 相互轉換完全指南:toArray、asList、陷阱與最佳實踐

     編輯:Java 程式設計入門

前言:為什麼轉換看似簡單卻暗藏玄機?

數組(T[])與集合(List<T>)是 Java 中兩種最基礎的數據結構。數組定長、支援基本類型、效能高;List 變長、泛型豐富、API 強大。實際開發中頻繁在兩者間轉換,但 toArray() 重載陷阱、Arrays.asList() 固定長度限制、基本類型數組無法直接轉換等問題,常導致運行時異常或邏輯 Bug。本文系統梳理轉換姿勢、核心原理、常見坑與生產級工具方法。

一、List → 數組:toArray() 重載選擇指南

1. 錯誤示範:無參 toArray()

List list = Arrays.asList("a", "b", "c");
Object[] objArr = list.toArray(); // 返回 Object[],不可直接轉 String[]
// String[] strArr = (String[]) objArr; // ClassCastException!

原因:無參版本返回 Object[],運行時類型擦除導致無法強轉。

2. 正確姿勢:傳入類型化數組

// 方式 A:精確大小數組(推薦,零拷貝)
String[] arr = list.toArray(new String[0]); // JDK 6+ 優化:大小為 0 也會新建合適大小數組

// 方式 B:預分配大小數組(舊版本優化)
String[] arr2 = list.toArray(new String[list.size()]);

原理(ArrayList 源碼):

public  T[] toArray(T[] a) {
    if (a.length < size)
        return (T[]) Arrays.copyOf(elementData, size, a.getClass()); // 新建同類型數組
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null; // 多餘位置置 null
    return a;
}
  • 傳入數組足夠大 → 直接填充返回原數組(多餘位置置 null)。
  • 傳入數組不夠大 → 反射創建同運行時類型的新數組(關鍵:保證類型安全)。
  • new String[0] 觸發第二分支,JVM 逃逸分析可優化分配,現代 JDK 推薦此寫法。

3. 基本類型 List 無法直接轉基本類型數組

List intList = Arrays.asList(1, 2, 3);
// int[] primitiveArr = intList.toArray(new int[0]); // 編譯錯誤:int[] 非 Object 子類
// 必須手動轉換
int[] arr = intList.stream().mapToInt(Integer::intValue).toArray();

二、數組 → List:Arrays.asList() 核心陷阱

1. 基本類型數組的「整體作為單元素」陷阱


int[] intArr = {1, 2, 3};
List list1 = Arrays.asList(intArr); // 推斷類型 List,單個元素!
System.out.println(list1.size()); // 1
System.out.println(list1.get(0) == intArr); // true

String[] strArr = {"a", "b", "c"};
List list2 = Arrays.asList(strArr); // 推斷 List,正常展開
System.out.println(list2.size()); // 3

原因:asList(T... a) 參數是泛型可變參數。int[] 是物件,匹配 T = int[];String[] 匹配 T = String(可變參數展開)。

2. 返回列表「固定長度、不可增刪」


List list = Arrays.asList("a", "b");
list.add("c"); // UnsupportedOperationException
list.remove(0); // UnsupportedOperationException
list.set(0, "x"); // ✅ 允許:修改現有元素
list.clear(); // UnsupportedOperationException

原因:返回的是 Arrays.ArrayList(內部類),繼承 AbstractList,未重寫 add/remove/clear,拋出 UnsupportedOperationException。

3. 底層數組共享:修改 List 影響原數組,反之亦然


String[] arr = {"a", "b"};
List list = Arrays.asList(arr);
list.set(0, "x");
System.out.println(arr[0]); // "x" —— 數組被修改!

arr[1] = "y";
System.out.println(list.get(1)); // "y" —— List 看到變化

本質:Arrays.ArrayList 內部直接持有傳入數組引用 private final E[] a,無拷貝。

三、生產級轉換工具方法

1. 數組 → 可變 List(獨立副本)


public static  List toMutableList(T[] array) {
    if (array == null) return Collections.emptyList();
    return new ArrayList<>(Arrays.asList(array)); // 拷貝構造
}

// 基本類型專用
public static List toMutableList(int[] array) {
    if (array == null) return Collections.emptyList();
    return Arrays.stream(array).boxed().collect(Collectors.toList());
}

2. List → 數組(空值安全、基本類型支援)


public static  T[] toArray(List list, Class clazz) {
    if (list == null || list.isEmpty()) {
        return (T[]) Array.newInstance(clazz, 0); // 返回空數組而非 null
    }
    return list.toArray((T[]) Array.newInstance(clazz, list.size()));
}

// 基本類型
public static int[] toPrimitiveArray(List list) {
    if (list == null) return new int[0];
    return list.stream().mapToInt(Integer::intValue).toArray();
}

3. Stream API 統一轉換(Java 8+ 推薦)


// List -> Array
String[] arr = list.toArray(String[]::new); // 方法引用,簡潔高效

// Array -> List (可變)
List list = Arrays.stream(array).collect(Collectors.toList());

// 基本類型
int[] intArr = list.stream().mapToInt(Integer::intValue).toArray();
List intList = Arrays.stream(intArr).boxed().collect(Collectors.toList());

四、效能對比與選型建議

場景推薦方式時間複雜度備註
List → 數組 (引用類型)list.toArray(new T[0])O(n)JDK 6+ 優化,零反射開銷
數組 → 可變 Listnew ArrayList<>(Arrays.asList(arr))O(n)雙重拷貝,但安全
數組 → 不可變視圖 (僅讀、極大數組)Arrays.asList(arr)O(1)零拷貝,共享底層數組,謹慎使用
基本類型數組 ↔ ListStream API boxed()/mapToInt()O(n)自動裝箱/拆箱開銷,無替代方案
高頻熱點路徑手寫循環拷貝 / System.arraycopyO(n)避免 Stream/Lambda 開銷,JIT 內聯最佳

五、常見面試題與避坑總結

  1. Q: list.toArray(new String[list.size()]) vs new String[0] 哪個快?
    A: 現代 JDK (8+) new String[0] 更優。逃逸分析可消除數組分配,直接在堆上分配精確大小。舊版本預分配大小避免 Arrays.copyOf 擴容拷貝。
  2. Q: 為什麼 Arrays.asList(1,2,3) 編譯通過但 Arrays.asList(intArr) 只有一個元素?
    A: 可變參數推斷。字面量 1,2,3 裝箱為 Integer,T=Integer;int[] 是物件,T=int[]。
  3. Q: 如何判斷 List 是 Arrays.asList 返回的固定長度列表?
    A: list.getClass().getName().contains("Arrays$ArrayList") 或嘗試 list.add(x) 捕獲異常(不推薦)。
  4. Q: Collections.emptyList() vs new ArrayList<>() 區別?
    A: 前者不可變單例,後者可變新實例。轉換工具方法返回空集合時優先用 Collections.emptyList()。

結語

數組與 List 轉換的核心難點在於:類型擦除導致的運行時類型缺失、基本類型與泛型的不兼容、固定長度視圖與可變副本的語義差異。掌握 toArray(T[]) 重載機制、Arrays.asList 返回類型特性、Stream API 統一轉換模式,並封裝空值安全、基本類型友好的工具方法,即可在業務代碼中游刃有餘。記住:**轉換即拷貝,拷貝即成本**——高頻熱點路徑請手寫優化,常規業務用標準庫保證可讀性。

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