| 維度 | Comparable (內部比較器) | Comparator (外部比較器) |
|---|---|---|
| 套件 | java.lang | java.util |
| 方法 | int compareTo(T o) | int compare(T o1, T o2) |
| 實作位置 | **類別自身**實作介面 | **獨立類別**實作介面 |
| 排序邏輯 | **自然順序** (唯一、固定) | **定製順序** (可多種、可變) |
| 適用場景 | 類別有明確預設排序 (如 ID、姓名) | 需多種排序、無法修改類別、第三方類別 |
| TreeSet/TreeMap Key | 直接支援 | 建構時傳入 new TreeSet<>(comparator) |
| Collections.sort() | sort(list) 自動用 compareTo | sort(list, comparator) 顯式指定 |
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 自然順序:按姓名字典序
@Override
public int compareTo(Person other) {
return this.name.compareTo(other.name); // 委託給 String 的比較
}
// Getter、toString 省略...
}
// 年齡升序
public class AgeAscComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return Integer.compare(p1.getAge(), p2.getAge()); // 避免溢位
}
}
// 年齡降序
public class AgeDescComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return Integer.compare(p2.getAge(), p1.getAge());
}
}
// 多欄位複合排序:先按年齡升序,年齡相同再按姓名降序
public class CompositeComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
int ageCmp = Integer.compare(p1.getAge(), p2.getAge());
if (ageCmp != 0) return ageCmp;
return p2.getName().compareTo(p1.getName()); // 姓名降序
}
}
public class SortDemo {
public static void main(String[] args) {
List<Person> list = Arrays.asList(
new Person("ccc", 20),
new Person("AAA", 30),
new Person("bbb", 10),
new Person("ddd", 40)
);
System.out.println("原始: " + list);
// 1. 自然順序 (Comparable)
Collections.sort(list);
System.out.println("姓名排序: " + list);
// [AAA-30, bbb-10, ccc-20, ddd-40]
// 2. 外部比較器:年齡升序
Collections.sort(list, new AgeAscComparator());
System.out.println("年齡升序: " + list);
// [bbb-10, ccc-20, AAA-30, ddd-40]
// 3. 外部比較器:年齡降序
Collections.sort(list, new AgeDescComparator());
System.out.println("年齡降序: " + list);
// [ddd-40, AAA-30, ccc-20, bbb-10]
// 4. Java 8+ Lambda 簡寫
list.sort(Comparator.comparingInt(Person::getAge)); // 升序
list.sort(Comparator.comparingInt(Person::getAge).reversed()); // 降序
list.sort(Comparator.comparing(Person::getName, String.CASE_INSENSITIVE_ORDER)); // 忽略大小寫
// 5. 複合排序
list.sort(Comparator
.comparingInt(Person::getAge)
.thenComparing(Person::getName, Comparator.reverseOrder()));
}
}
強烈建議:compareTo() 返回 0 應該意味著 equals() 返回 true。
// 正確:比較邏輯與 equals 一致
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person p = (Person) o;
return age == p.age && Objects.equals(name, p.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
違反此契約會導致 TreeSet、TreeMap 行為異常 (如無法移除元素、重複鍵)。
Comparator<Person> cmp = Comparator
.comparing(Person::getName) // 主鍵:姓名
.thenComparingInt(Person::getAge) // 次鍵:年齡升序
.thenComparing(Person::getId, Comparator.nullsLast(Comparator.naturalOrder())); // 第三鍵:ID,null 最後
// 反轉
cmp.reversed();
// null 安全
Comparator.nullsFirst(Comparator.naturalOrder());
Comparator.nullsLast(Comparator.reverseOrder());
Comparator.comparing... 鏈式 API 讓複合排序極其優雅