在 Java 集合框架中,排序是高頻操作。Comparable 與 Comparator 兩大接口雖同為比較契約,但設計意圖、使用場景與實現機制截然不同。本文從源碼層面剖析兩者差異,並給出生產級選型指南。
| 維度 | Comparable | Comparator |
|---|---|---|
| 包位置 | java.lang | java.util |
| 方法簽名 | int compareTo(T o) |
int compare(T o1, T o2) |
| 設計模式 | 內部比較(自然順序) | 策略模式(外部比較器) |
| 修改類源碼 | 需實現接口,侵入性強 | 外部實現,零侵入 |
| 適用場景 | 類具有單一、固有的自然順序 | 多種排序策略、無法修改源碼、臨時排序 |
實現 Comparable 的類聲明:「我支持自我比較」。JDK 中 String、Integer、Date 等均實現了此接口。
public class Person implements Comparable<Person> {
private String name;
private int age;
@Override
public int compareTo(Person other) {
// 首先按年齡升序,年齡相同則按姓名字典序
int ageDiff = Integer.compare(this.age, other.age);
return (ageDiff != 0) ? ageDiff : this.name.compareTo(other.name);
}
}
List<Person> people = new ArrayList<>();
// ... 添加元素
Collections.sort(people); // 直接排序,無需傳入 Comparator
Comparator 將比較邏輯從被比較對象中剝離,符合「開閉原則」。當需要多種排序規則、或無法修改目標類(如第三方庫類、最終類)時極其有用。
public class AbsComparator implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
int v1 = Math.abs(o1);
int v2 = Math.abs(o2);
return Integer.compare(v1, v2);
}
}
// 使用
Integer[] nums = { -5, 3, -2, 8, -1 };
Arrays.sort(nums, new AbsComparator());
// 結果:-1, -2, 3, -5, 8
// 按長度降序排序字符串
strings.sort((s1, s2) -> Integer.compare(s2.length(), s1.length()));
// 多條件鏈式排序:先按年齡升序,再按姓名降序
people.sort(Comparator.comparing(Person::getAge)
.thenComparing(Person::getName, Comparator.reverseOrder()));
強烈建議:compareTo 返回 0 應意味著 equals 返回 true。違反此約定會導致 TreeSet、TreeMap 等依賴比較器的集合行為異常(如無法正確檢測重複元素)。
Comparator 接口聲明了 equals(Object obj),但實現類通常無需覆寫。默認繼承自 Object 的引用比較即可,除非你需要基於比較邏輯語義判斷兩個比較器是否等價(極少見)。
Comparable 定義對象的內在自然順序,屬於類的核心契約。Comparator 定義外部可插拔的比較策略,體現策略模式精髓。深入理解兩者邊界,能讓你在集合排序、流處理、數據結構選型等場景中做出最優架構決策。