java.util.Comparator 介面雖簡單(僅需實作 compare(Object o1, Object o2)),但其 API 文件規定的契約義務常被忽略,導致排序行為異常或序列化失敗。本文詳細解析三大核心契約。
compare(a, b) 與 compare(b, a) 結果必須相反:一正一負,或皆為零compare(a, b) 拋 ClassCastException,則 compare(b, a) 也必須拋同類型例外public int compare(Object o1, Object o2) {
if (o1 instanceof Long) {
Long ln = (Long) o1;
return ln.compareTo(o2); // o2 非 Long 時拋 ClassCastException
}
return 0;
}
a = new Long(5), b = "Text" 時:compare(a, b) 拋例外,compare(b, a) 回傳 0 —— 違反對稱性。
TreeSet、TreeMap 儲存 Comparator 以維持排序,若集合需序列化,Comparator 必須實作 Serializable。否則序列化集合時會拋 NotSerializableException。
當 Comparator 有多種比較模式(如依名稱、依年齡、依薪資排序)時,應覆寫 equals(Object obj),讓集合能正確判斷兩個 Comparator 是否相等(影響 TreeSet/TreeMap 合併、比較邏輯)。
public class ExampleComparator implements Comparator, Serializable {
private int type; // 1: 依名稱, 2: 依年齡
public ExampleComparator(int type) { this.type = type; }
public int compare(Object o1, Object o2) {
if (type == 1) { /* 依名稱比較 */ }
else if (type == 2) { /* 依年齡比較 */ }
return 0;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof ExampleComparator)) return false;
ExampleComparator other = (ExampleComparator) obj;
return this.type == other.type;
}
}
compare 回傳值正負對稱,例外拋擲對稱Serializable(若用於可序列化集合)equals(含 hashCode 最佳實務)Comparator 看似簡單,但契約義務關係到排序正確性、集合序列化與邏輯一致性。實作時務必閱讀 API 文件並遵守三大契約,避免難以除錯的潛在缺陷。