首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 核心 API 深度解析:Comparable、Comparator 與有序集合

     編輯:Java 相關知識

物件排序是資料處理的基石。Java 透過 Comparable(自然順序)與 Comparator(外部比較器)雙軌制,配合 SortedSet/SortedMap 實現強大的有序集合體系。本文從介面定義、實作範例、進階技巧到有序介面完整剖析。

Comparable:自然順序的契約

public interface Comparable<T> {
    int compareTo(T o);
}

回傳負/零/正分別代表「小於/等於/大於」。String、Date、包裝類型等 JDK 核心類均實作此介面。

標準實作範本:Name 類別

public class Name implements Comparable<Name> {
    private final String firstName, lastName; // 不變性

    public Name(String first, String last) {
        if (first == null || last == null) throw new NullPointerException();
        this.firstName = first; this.lastName = last;
    }

    // equals/hashCode 必須一致
    public boolean equals(Object o) { ... }
    public int hashCode() { return 31*firstName.hashCode() + lastName.hashCode(); }
    public String toString() { return firstName + " " + lastName; }

    // 比較邏輯:姓氏優先,再名字
    public int compareTo(Name n) {
        int lastCmp = lastName.compareTo(n.lastName);
        return (lastCmp != 0) ? lastCmp : firstName.compareTo(n.firstName);
    }
}

關鍵點:

  • 不可變物件:作為 Set 元素/Map 鍵的必要條件
  • equals/hashCode/compareTo **三者語義一致**
  • compareTo 標準模式:主要鍵比較 → 次要鍵 → …
  • 拋出 ClassCastException 處理型別不符(而非回傳 false)

排序演示

List<Name> list = Arrays.asList(
    new Name("John", "Lennon"),
    new Name("Karl", "Marx"),
    new Name("Groucho", "Marx"),
    new Name("Oscar", "Grouch")
);
Collections.sort(list);
// [Oscar Grouch, John Lennon, Groucho Marx, Karl Marx]

Comparator:外部排序策略


public interface Comparator<T> {
    int compare(T o1, T o2);
}

封裝排序邏輯,適用於:

  • 非自然順序排序
  • 不實作 Comparable 的類別
  • 同一類別多種排序需求

案例:員工資歷排序


// EmployeeRecord 自然順序按姓名排序
// 需求:按雇用日期倒序(資歷深者優先)

// 版本 1:簡單但有缺陷
Comparator<EmployeeRecord> SENIORITY_ORDER = (r1, r2) ->
    r2.hireDate().compareTo(r1.hireDate()); // 倒序

// 缺陷:雇用日相同視為「相等」,TreeSet 會視為重複元素丟棄

修正:實作 Total Ordering(全序)

Comparator<EmployeeRecord> SENIORITY_ORDER = (r1, r2) -> {
    int dateCmp = r2.hireDate().compareTo(r1.hireDate());
    if (dateCmp != 0) return dateCmp;
    // 次要鍵:員工編號唯一識別
    return Integer.compare(r1.employeeNumber(), r2.employeeNumber());
};

陷阱警示:切勿用 r1.num() - r2.num() 替代 Integer.compare —— 整數溢出會違反傳遞性,導致極難排查的錯誤。

SortedSet 介面完整解析

public interface SortedSet<E> extends Set<E> {
    // 區間視圖
    SortedSet<E> subSet(E from, E to);      // [from, to)
    SortedSet<E> headSet(E to);              // [首, to)
    SortedSet<E> tailSet(E from);            // [from, 尾]

    // 端點
    E first(); E last();

    // 比較器存取
    Comparator<? super E> comparator(); // 自然順序回傳 null
}

區間視圖特性

  • **即時視圖**:底層集合修改即時反映,端點為元素空間絕對位置(非索引)
  • **長期有效**:異於 List.subList,可長期持有使用
  • 半開區間 [low, high),含低不含高

實用技巧


// 統計 "doorbell" 到 "pickle" 之間單詞數(含低不含高)
int count = dict.subSet("doorbell", "pickle").size();

// 刪除所有以 'f' 開頭的元素
dict.subSet("f", "g").clear();

// 閉區間 [low, high] 技巧:high 後附加 '\0'
int count = dict.subSet("doorbell", "pickle\0").size();

// 開區間 (low, high) 技巧:low 後附加 '\0'
int count = dict.subSet("doorbell\0", "pickle").size();

// 分冊視圖
SortedSet<String> vol1 = dict.headSet("n"); // a-m
SortedSet<String> vol2 = dict.tailSet("n"); // n-z

端點操作與反向迭代難題

  • first()/last() 取首尾元素
  • 反向迭代無直接支援,慣用語:ss.headSet(o).last() 取得 o 的前驅,但效率低(每次查找)

SortedMap 介面:Map 視角的完全對等

public interface SortedMap<K,V> extends Map<K,V> {
    Comparator<? super K> comparator();
    SortedMap<K,V> subMap(K from, K to);
    SortedMap<K,V> headMap(K to);
    SortedMap<K,V> tailMap(K from);
    K firstKey(); K lastKey();
}

所有 SortedSet 慣用語透過「鍵/值/項視圖」直接映射至 SortedMap,僅需微調型別。

標準建構子約定

SortedSet/SortedMap 實現類必須提供:

  1. 無參建構子:自然順序
  2. Comparator 參數建構子:指定比較器
  3. SortedSet/Map 參數建構子:複製並保持相同排序

**注意**:建構子選擇基於**編譯時期參數型別**,非執行期型別。

掌握比較器鏈、全序實作與區間視圖技巧,能讓有序集合操作既優雅又高效。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved