java.util.Arrays.sort() 是 Java 開發者最高頻調用的庫方法之一。但許多開發者僅知「能排序」,不知雙軸快排、TimSort、基本類型 vs 引用類型分流、比較器契約、穩定性、性能陷阱。本文將基於 JDK 17/21 源碼,徹底講透排序實現、API 選型、避坑指南、效能調優。
| 類型 | 算法 | 穩定性 | 時間複雜度 | 空間複雜度 | JDK 版本演進 |
|---|---|---|---|---|---|
| 基本類型 (int[], double[]...) | Dual-Pivot Quicksort (雙軸快排) | 不穩定 | 平均 O(n log n) 最壞 O(n²) | O(log n) 棧 | JDK 7 引入,替換單軸快排 |
| 引用類型 (Object[], T[]) | TimSort (合併插入混合) | 穩定 | O(n log n) | O(n) 輔助陣列 | JDK 7 引入,替換合併排序 |
傳統單軸快排:選一個 pivot,分區為 < pivot 和 > pivot。
雙軸快排:選兩個 pivot P1 < P2,分區為三段:
[ < P1 ] [ P1 ≤ x ≤ P2 ] [ > P2 ] ↑ ↑ ↑ left center right
源自 Python,結合歸併排序與插入排序:
minRun (32-64)run[n-2] > run[n-1] + run[n] 且 run[n-1] > run[n]galloping mode(指數搜索)加速穩定性保證:合併時左 Run 元素優先,相等不交換位置。
// 基本類型:僅升序,無 Comparator 重載
Arrays.sort(int[] a)
Arrays.sort(int[] a, int fromIndex, int toIndex) // 區間排序
Arrays.parallelSort(int[] a) // JDK 8+ 並行排序,大數據量顯著加速
// 引用類型:天然支援 Comparator
Arrays.sort(Object[] a)
Arrays.sort(T[] a, Comparator super T> c)
Arrays.sort(T[] a, int fromIndex, int toIndex, Comparator super T> c)
Arrays.parallelSort(T[] a, Comparator super T> c)
| 方案 | 代碼 | 優點 | 缺點 |
|---|---|---|---|
| 包裝類 + Comparator | Arrays.sort(boxedArray, Collections.reverseOrder()) | 語義清晰 | 自動裝箱/拆箱開銷大,內存佔用高 |
| 升序後反轉 | Arrays.sort(arr); reverse(arr); | 零額外內存,極快 | 需額外 O(n) 反轉步驟 |
| 自定義快排 | 複製源碼改比較邏輯 | 單遍歷完成 | 維護成本高,易出錯 |
推薦:升序後反轉,代碼簡潔、效能最優:
public static void sortDesc(int[] arr) {
Arrays.sort(arr);
for (int i = 0, j = arr.length - 1; i < j; i++, j--) {
int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;
}
}
// 錯誤:違反傳遞性
Comparator bad = (u1, u2) -> u1.getAge() - u2.getAge(); // 整數溢出風險!
// 正確:
Comparator good = Comparator.comparingInt(User::getAge);
// 或
Comparator good = (u1, u2) -> Integer.compare(u1.getAge(), u2.getAge());
| 契約 | 要求 | 違規後果 |
|---|---|---|
| 自反性 | compare(x, x) == 0 | 拋出 IllegalArgumentException |
| 反對稱性 | sign(compare(x,y)) == -sign(compare(y,x)) | 排序結果不可預測、死循環 |
| 傳遞性 | compare(x,y)>0 && compare(y,z)>0 → compare(x,z)>0 | TimSort 合併階段拋出異常 |
| 一致性 | compare(x,y)==0 → equals(x,y) (建議) | TreeSet/TreeMap 行為異常 |
// 編譯錯誤:無 Comparator 重載
// Arrays.sort(intArray, Comparator.reverseOrder());
a - b 整數溢出
// 錯誤:Integer.MAX_VALUE - (-1) 溢出為負
// 正確:
Integer.compare(a, b) // JDK 7+
Comparator.comparingInt(...)
int[] original = {3,1,2};
int[] copy = original.clone(); // 或 Arrays.copyOf(original, original.length)
Arrays.sort(copy);
// original 保持不變
String[] arr = {"b", null, "a"};
Arrays.sort(arr); // NPE
// 解決:
Arrays.sort(arr, Comparator.nullsFirst(Comparator.naturalOrder()));
// 或
Arrays.sort(arr, (a,b) -> a==null ? (b==null?0:-1) : (b==null?1:a.compareTo(b)));
class User { String name; } // 未實現 Comparable
User[] users = ...;
Arrays.sort(users); // ClassCastException
// 解決:實現 Comparable 或傳入 Comparator
| 方法 | 隨機數據 | 有序數據 | 逆序數據 | 重複度高 |
|---|---|---|---|---|
| Arrays.sort | ~1.2s | ~0.15s | ~0.18s | ~0.08s |
| Arrays.parallelSort | ~0.35s | ~0.12s | ~0.14s | ~0.06s |
| Collections.sort (List) | ~1.5s | ~0.2s | ~0.25s | ~0.1s |
需要排序?
├─ 基本類型?
│ ├─ 升序 → Arrays.sort / parallelSort(大數據)
│ └─ 降序 → Arrays.sort + 反轉
└─ 引用類型?
├─ 自然序 (實現 Comparable) → Arrays.sort
├─ 自定義序 → Arrays.sort(array, comparator)
├─ 多條件 → Comparator.comparing().thenComparing()
├─ 含 null → Comparator.nullsFirst/Last()
├─ 大數據 (>100萬) → parallelSort
└─ 需保持原數組 → copyOf 後排序
掌握 Arrays.sort 的算法分流邏輯、Comparator 契約、基本類型降序技巧、並行排序閾值、null 安全處理,即可在面試、調優、架構設計中游刃有餘。