| 維度 | Comparable(內部比較器) | Comparator(外部比較器) |
|---|---|---|
| 定義位置 | 實體類別內部 implements Comparable<T> | 獨立類別 implements Comparator<T> 或 Lambda |
| 比較邏輯 | 自然順序(單一、固定) | 自訂順序(多種、可換裝) |
| 核心方法 | int compareTo(T o) | int compare(T o1, T o2) |
| 調用方式 | Collections.sort(list) / Arrays.sort(array) | Collections.sort(list, comparator) / Arrays.sort(array, comparator) |
| 適用場景 | 類別具備明確「自然順序」(如 ID、時間、字典序) | 需多種排序規則、無法修改實體類別、第三方類別 |
| 執行緒安全 | 無狀態,安全 | 無狀態,安全(建議單例或靜態常量) |
public class User implements Comparable<User> {
private Long id;
private String name;
private LocalDateTime createTime;
// Getter/Setter 省略
@Override
public int compareTo(User other) {
// 1. 空值防護
if (other == null) return 1;
// 2. 主鍵 ID 自然順序(避免長整數溢出用 Long.compare)
return Long.compare(this.id, other.id);
// 若需複合排序:先按時間倒序,再按 ID 正序
// int cmp = other.getCreateTime().compareTo(this.createTime);
// return cmp != 0 ? cmp : Long.compare(this.id, other.id);
}
}
List<User> users = ...; Collections.sort(users); // 呼叫 compareTo // 或 users.sort(null); // Java 8+ List.sort(null) 使用自然順序
a.compareTo(b) == -b.compareTo(a)a > b && b > c → a > ca.compareTo(b) == 0 → a.equals(b)(強烈建議,否則 TreeSet/TreeMap 行為違反預期)Comparator<User> byNameLength = new Comparator<User>() {
@Override
public int compare(User u1, User u2) {
if (u1 == null || u2 == null) return 0;
return Integer.compare(u1.getName().length(), u2.getName().length());
}
};
// 單一欄位
Comparator<User> byId = Comparator.comparingLong(User::getId);
// 復合排序:建立時間倒序 -> 名稱正序
Comparator<User> complex = Comparator
.comparing(User::getCreateTime, Comparator.reverseOrder())
.thenComparing(User::getName);
// 空值處理:nullsFirst / nullsLast
Comparator<User> safe = Comparator
.nullsLast(Comparator.comparing(User::getName));
List<User> users = ...;
// 場景 1:管理後台預設按 ID 排序(自然順序)
users.sort(null);
// 場景 2:前端需求「按註冊時間倒序」
users.sort(Comparator.comparing(User::getCreateTime).reversed());
// 場景 3:導出報表「部門優先,部門內按薪資倒序」
users.sort(Comparator
.comparing(User::getDeptId)
.thenComparing(Comparator.comparingLong(User::getSalary).reversed()));
// 場景 4:第三方類別無法實作 Comparable
List<ThirdPartyDTO> dtos = ...;
dtos.sort(Comparator.comparing(ThirdPartyDTO::getCode));
| 資料結構 | 自然順序 | 自訂比較器 |
|---|---|---|
List<T> | Collections.sort(list)list.sort(null) (Java 8+) | Collections.sort(list, cmp)list.sort(cmp) |
T[] | Arrays.sort(array) | Arrays.sort(array, cmp) |
基本型別陣列 int[] | Arrays.sort(array) | 不支援(需轉包裝型別或自寫排序) |
Comparator.reverseOrder() / .reversed()users.sort(Comparator.comparingLong(User::getId).reversed());
nullsFirst() / nullsLast()// 部門升序 -> 年齡降序 -> 姓名升序
users.sort(Comparator
.comparingInt(User::getDeptId)
.thenComparingInt(User::getAge).reversed()
.thenComparing(User::getName));
Map<String, Integer> map = ...; // 按 Value 降序輸出前 10 map.entrySet().stream() .sorted(Map.Entry.<String, Integer>comparingByValue().reversed()) .limit(10) .forEach(e -> System.out.println(e.getKey() + "=" + e.getValue()));
Integer.compare / Long.compare// 錯誤寫法(可能溢出) return this.id - other.id; // 正確寫法 return Long.compare(this.id, other.id);
compareTo 回傳 0 但 equals 回傳 false,會導致 Set 允許重複元素、Map 覆寫失效。原則:compareTo 一致性應與 equals 保持一致。Arrays.sort(int[], Comparator) 不存在,需轉 Integer[] 或用並行排序 Arrays.parallelSort。.compareTo() 拋 NPE。統一用 Comparator.nullsFirst/Last 包裝。Collections.sort 為 TimSort (O(n log n)),穩定排序。超大列表考慮分頁查詢時在 SQL 層排序,避免記憶體壓力。能改原始碼、且業務上有明確「預設排序」 → 實作
Comparable(如 ID、時間、編號)。需要多種排序、無法修改實體、第三方類別、臨時邏輯 → 寫
Comparator(Lambda/方法引用/靜態工廠)。兩者並不互斥:實體實作
Comparable提供預設行為,業務層按需注入Comparator覆蓋或組合。
掌握 Comparable 與 Comparator 的精髓,在於理解「自然順序是領域模型的內在屬性,自訂順序是應用層的策略選擇」。善用 Java 8+ 函數式 API,讓排序邏輯既清晰又可組合,是寫出優雅、可維護 Java 代碼的關鍵技能之一。