List、Set、Array 是 Java 開發中最基礎的三種資料容器。它們之間的轉換雖然 API 簡單,但涉及型別安全、效能、空值處理等細節。本文系統整理所有轉換場景,提供標準寫法與避坑指南。
List list = Arrays.asList("a", "b", "c", "d");
// 無參 toArray() → Object[],需強轉且不安全
Object[] objArr = list.toArray();
// 有參 toArray(T[]) → 型別安全,推薦
String[] strArr1 = list.toArray(new String[0]); // JVM 自動建立適當大小陣列(JDK 11+ 最佳)
String[] strArr2 = list.toArray(new String[list.size()]); // 精確大小,避免擴容/截斷
最佳實務:優先使用 toArray(new String[0]),JDK 11+ 會直接配置精確大小陣列,效能最優且程式碼最簡潔。
String[] arr = {"a", "b", "c"};
// Arrays.asList() → 固定大小 List,底層仍是原陣列,不可增刪
List fixedList = Arrays.asList(arr);
// fixedList.add("e"); // 拋出 UnsupportedOperationException
// 可變 List:複製到新 ArrayList
List mutableList = new ArrayList<>(Arrays.asList(arr));
// 或 Java 10+ List.of() 不可變,再包裝
List mutableList2 = new ArrayList<>(List.of(arr));
Set set = new HashSet<>(Arrays.asList("a", "b"));
// 同 List,推薦有參版本
Object[] objArr = set.toArray();
String[] strArr = set.toArray(new String[0]);
String[] arr = {"a", "b", "c", "a"}; // 含重複
// 方式 1:Arrays.asList() + HashSet 建構子(去重)
Set set1 = new HashSet<>(Arrays.asList(arr));
// 方式 2:Stream API(Java 8+,更靈活)
Set set2 = Arrays.stream(arr).collect(Collectors.toSet());
// 方式 3:手動 addAll
Set set3 = new HashSet<>();
Collections.addAll(set3, arr); // 高效批量加入
List list = Arrays.asList("a", "b", "a", "c");
Set set = new HashSet<>(list); // 無序去重
Set linkedSet = new LinkedHashSet<>(list); // 保留首次出現順序去重
Set treeSet = new TreeSet<>(list); // 自然序去重
Set set = new HashSet<>(Arrays.asList("a", "b"));
List list = new ArrayList<>(set); // 順序不定
// 若需排序
List sorted = new ArrayList<>(set);
Collections.sort(sorted);
| 場景 | 陷阱 | 正確做法 |
|---|---|---|
基本型別陣列 int[] 轉 List | Arrays.asList(int[]) 回傳 List(單一元素是陣列) | 用 IntStream.of(arr).boxed().collect(Collectors.toList()) 或 Apache Commons ArrayUtils.toObject() |
Listint[] | 無直接 API | list.stream().mapToInt(Integer::intValue).toArray() |
| 保留順序去重 | 直接 new HashSet(list) 亂序 | new LinkedHashSet(list) |
| 固定大小 List 增刪 | Arrays.asList() 回傳固定大小 List | 包裝 new ArrayList<>(Arrays.asList(...)) |
| 多執行緒環境共用集合 | 非同步集合導致併發異常 | 用 Collections.synchronizedList/Set 或 CopyOnWriteArrayList/ConcurrentHashMap.newKeySet() |
| 空值處理 | 部分實作不允許 null(TreeSet、ConcurrentHashMap key) | 明確檢查或使用允許 null 的實作 |
// 任意 Collection → List
List list = collection.stream().collect(Collectors.toList());
// 任意 Collection → Set
Set set = collection.stream().collect(Collectors.toSet());
// 任意 Collection → 數組
String[] arr = collection.stream().toArray(String[]::new);
// Array → Stream → 任意集合
List list2 = Arrays.stream(array).collect(Collectors.toList());
Set set2 = Arrays.stream(array).collect(Collectors.toSet());
Stream 管道提供統一、可組合、支援平行處理的轉換方式,建議新專案作為首選。
| 轉換 | 時間複雜度 | 空間複雜度 | 備註 |
|---|---|---|---|
| List ↔ Array (toArray/ArrayList建構) | O(n) | O(n) | 需複製元素引用 |
| Set → Array | O(n) | O(n) | 同 List |
| Array → Set (HashSet建構) | O(n) 平均 | O(n) | 含雜湊計算與去重 |
| List → Set (HashSet建構) | O(n) 平均 | O(n) | 去重開銷 |
| Stream 收集 | O(n) | O(n) | 額外管道物件開銷微乎其微 |
掌握 List、Set、Array 互轉的標準 API 與陷阱,能顯著減少樣板碼與執行期錯誤。核心原則:優先使用有參 toArray(T[]) 保證型別安全、區分固定大小與可變 List、善用 Stream API 統一風格、基本型別陣列必須用 Stream/工具類轉換。將常用轉換封裝為專案工具類 CollectionUtils,進一步提升團隊開發效率。