Java Comparable 與 Comparator

ComparableComparator 是 Java 中用於比較和排序物件的兩個介面。

Comparable

物件實作 Comparable 介面來定義自然排序

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.age - other.age;  // 依年齡排序
    }
}

// 使用
List<Person> people = new ArrayList<>();
people.add(new Person("Alice", 25));
people.add(new Person("Bob", 20));
people.add(new Person("Charlie", 30));

Collections.sort(people);  // 使用 compareTo 排序
// 結果:Bob(20), Alice(25), Charlie(30)

compareTo 回傳值

回傳值意義
負數this < other
0this == other
正數this > other

Comparator

Comparator 是獨立的比較器,用於提供外部排序邏輯

// 依名字排序的 Comparator
Comparator<Person> byName = new Comparator<Person>() {
    @Override
    public int compare(Person p1, Person p2) {
        return p1.getName().compareTo(p2.getName());
    }
};

// Lambda 寫法
Comparator<Person> byName = (p1, p2) -> p1.getName().compareTo(p2.getName());

// 方法參考
Comparator<Person> byName = Comparator.comparing(Person::getName);

// 使用
Collections.sort(people, byName);

Comparator 工廠方法

// comparing - 依屬性排序
Comparator<Person> byAge = Comparator.comparing(Person::getAge);
Comparator<Person> byName = Comparator.comparing(Person::getName);

// 反向排序
Comparator<Person> byAgeDesc = Comparator.comparing(Person::getAge).reversed();

// 多欄位排序
Comparator<Person> byAgeAndName = Comparator
    .comparing(Person::getAge)
    .thenComparing(Person::getName);

// null 處理
Comparator<Person> nullsFirst = Comparator.nullsFirst(byAge);
Comparator<Person> nullsLast = Comparator.nullsLast(byAge);

// 自然排序
Comparator<String> natural = Comparator.naturalOrder();
Comparator<String> reverse = Comparator.reverseOrder();

數值比較

// 避免溢位問題
Comparator<Person> byAge = Comparator.comparingInt(Person::getAge);
Comparator<Account> byBalance = Comparator.comparingLong(Account::getBalance);
Comparator<Product> byPrice = Comparator.comparingDouble(Product::getPrice);

在各種場景使用

Collections.sort()

List<Person> people = new ArrayList<>();
Collections.sort(people);                // 使用 Comparable
Collections.sort(people, byName);        // 使用 Comparator

List.sort()

people.sort(byAge);
people.sort(Comparator.comparing(Person::getName).reversed());

Stream.sorted()

people.stream()
    .sorted(Comparator.comparing(Person::getAge))
    .collect(Collectors.toList());

TreeSet / TreeMap

// 使用 Comparable
TreeSet<Person> set1 = new TreeSet<>();

// 使用 Comparator
TreeSet<Person> set2 = new TreeSet<>(byName);
TreeMap<Person, String> map = new TreeMap<>(byAge);

PriorityQueue

// 最小堆(預設)
PriorityQueue<Person> minHeap = new PriorityQueue<>(byAge);

// 最大堆
PriorityQueue<Person> maxHeap = new PriorityQueue<>(byAge.reversed());

Comparable vs Comparator

特性ComparableComparator
位置類別內部類別外部
方法compareTocompare
排序方式自然排序(唯一)可定義多種
修改類別需要不需要

實用範例

多條件排序

List<Person> people = Arrays.asList(
    new Person("Alice", 25),
    new Person("Bob", 25),
    new Person("Alice", 30)
);

// 先依年齡,再依名字
people.sort(Comparator
    .comparing(Person::getAge)
    .thenComparing(Person::getName));

// Alice(25), Bob(25), Alice(30)

處理 null

List<String> items = Arrays.asList("B", null, "A", null, "C");

items.sort(Comparator.nullsLast(Comparator.naturalOrder()));
// [A, B, C, null, null]

items.sort(Comparator.nullsFirst(Comparator.naturalOrder()));
// [null, null, A, B, C]

自訂排序規則

// 字串依長度排序,長度相同依字母排序
Comparator<String> byLengthThenAlpha = Comparator
    .comparingInt(String::length)
    .thenComparing(Comparator.naturalOrder());

List<String> words = Arrays.asList("cat", "elephant", "dog", "ant");
words.sort(byLengthThenAlpha);
// [ant, cat, dog, elephant]

重點整理

  • Comparable 定義物件的自然排序,實作 compareTo()
  • Comparator 提供外部排序邏輯,實作 compare()
  • 使用 Comparator.comparing() 簡潔地建立比較器
  • thenComparing() 串接多個排序條件
  • reversed() 反轉排序順序
  • nullsFirst() / nullsLast() 處理 null 值