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

Java ArrayList 排序完全指南:Comparator、Comparable 與 Lambda 實戰

     編輯:Java 相關知識

前言

對 ArrayList 排序是業務開發高頻操作。Java 提供 Collections.sort()、List.sort()、Stream.sorted() 多種途徑,核心在於 比較策略 的定義:Comparable(自然順序)vs Comparator(定製順序)。本文通過完整代碼示範四種排序模式,並解析 PriorityQueue 排序關聯。

準備:領域模型


// 普通類,不實現 Comparable
class Term {
    String name; int id;
    Term(String name, int id) { this.name = name; this.id = id; }
    @Override public String toString() { return name + "(" + id + ")"; }
}

// 實現 Comparable(自然順序:id 降序)
class STerm implements Comparable<STerm> {
    String name; int id;
    STerm(String name, int id) { this.name = name; this.id = id; }
    @Override public int compareTo(STerm o) { return o.id - this.id; } // 降序
    @Override public String toString() { return name + "(" + id + ")"; }
}

四種排序模式實戰

模式一:外部 Comparator 類(傳統、可復用)


class TermComparator implements Comparator<Term> {
    @Override public int compare(Term t1, Term t2) {
        return t1.id - t2.id; // id 升序
    }
}

// 使用
List<Term> list = new ArrayList<>();
list.add(new Term("a", 1));
list.add(new Term("b", 5));
list.add(new Term("c", 2));
Collections.sort(list, new TermComparator());
// 結果:[a(1), c(2), b(5)]

模式二:匿名內部類 / Lambda(就地定製,最常用)


// 匿名內部類(JDK 7 風格)
Collections.sort(list, new Comparator<Term>() {
    @Override public int compare(Term t1, Term t2) {
        return t2.id - t1.id; // 降序
    }
});

// Lambda(JDK 8+ 推薦,極其簡潔)
list.sort((t1, t2) -> t1.id - t2.id);          // 升序
list.sort((t1, t2) -> t2.id - t1.id);          // 降序
list.sort(Comparator.comparingInt(t -> t.id)); // 方法引用升序
list.sort(Comparator.comparingInt(Term::getId).reversed()); // 降序

模式三:Comparator 實例復用(線程安全、無狀態)


// 靜態常量,全局復用
static final Comparator<Term> TERM_BY_ID_ASC = Comparator.comparingInt(Term::getId);
static final Comparator<Term> TERM_BY_ID_DESC = TERM_BY_ID_ASC.reversed();
static final Comparator<Term> TERM_BY_NAME_THEN_ID = 
    Comparator.comparing(Term::getName).thenComparingInt(Term::getId);

// 使用
list.sort(TERM_BY_ID_ASC);
list.sort(TERM_BY_NAME_THEN_ID);

模式四:元素實現 Comparable(自然順序,零配置)


List<STerm> slist = new ArrayList<>();
slist.add(new STerm("a", 1));
slist.add(new STerm("b", 5));
slist.add(new STerm("c", 2));
Collections.sort(slist); // 或 slist.sort(null)
// 結果:[b(5), c(2), a(1)]  —— 依照 compareTo 降序

進階:多鍵排序、空值處理、穩定性

多鍵排序(thenComparing 鏈式)


// 先按名稱升序,名稱相同再按 id 降序
list.sort(
    Comparator.comparing(Term::getName)
              .thenComparingInt(Term::getId).reversed()
);

空值安全


// 空值排前/排後
list.sort(Comparator.nullsFirst(Comparator.comparingInt(Term::getId)));
list.sort(Comparator.nullsLast(Comparator.comparingInt(Term::getId)));

// 實體類 getter 返回 Optional
list.sort(Comparator.comparing(t -> t.getOptId(), Comparator.nullsLast(Integer::compare)));

排序穩定性

  • Collections.sort / List.sort 使用 TimSort(歸併+插入),**穩定排序**:相等元素相對位置不變。
  • 穩定性對多鍵排序至關重要:先按次要鍵排序,再按主要鍵排序,等價於單次多鍵排序。

PriorityQueue 排序關聯

PriorityQueue 內部為最小堆,出隊順序即排序順序。三種定製方式:

方式一:元素實現 Comparable


class WordFreq implements Comparable<WordFreq> {
    String word; int freq;
    @Override public int compareTo(WordFreq o) { return o.freq - this.freq; } // 最大堆效果
}
PriorityQueue<WordFreq> pq = new PriorityQueue<>();
pq.offer(new WordFreq("aaa", 3));
pq.offer(new WordFreq("bbb", 4));
while (!pq.isEmpty()) System.out.println(pq.poll()); // 頻率高先出

方式二:構造器傳 Comparator


PriorityQueue<Term> pq = new PriorityQueue<>(Comparator.comparingInt(Term::getId));

方式三:Lambda 構造(JDK 8+)


PriorityQueue<Term> pq = new PriorityQueue<>((t1, t2) -> t1.id - t2.id);

⚠️ 陷阱:for-each 遍歷無序


// ❌ 錯誤:for-each 不保證順序,內部數組存儲順序 ≠ 堆序
for (WordFreq wf : pq) System.out.println(wf); 

// ✅ 正確:poll() 依次取出堆頂
while (!pq.isEmpty()) System.out.println(pq.poll());

String/List 等內建類型排序

  • String、Integer、Date 等已實現 Comparable,直接 list.sort(null) 或 Collections.sort(list) 即可自然順序。
  • 自定義類建議實現 Comparable 定義自然順序,複雜場景再用 Comparator 覆蓋。

效能與避坑提示

場景建議
大列表(>10萬)優先 list.sort(comparator)(原地排序),避免 Stream.sorted() 產生新流
頻繁排序同一列表復用 static final Comparator,避免重複創建 Lambda 實例
基本類型排序用 Comparator.comparingInt/Long/Double 避免裝箱
並行排序JDK 8+ Arrays.parallelSort()(陣列),List 需先 toArray
自定義 compareTo 邏輯錯誤必須滿足:自反性、對稱性、傳遞性;違反會導致排序異常或死循環

總結

  1. 自然順序:類實現 Comparable,list.sort(null)。
  2. 定製順序:list.sort(Comparator.comparing(...))(Lambda/方法引用首選)。
  3. 復用比較器:定義 static final Comparator 常量。
  4. PriorityQueue:構造器傳 Comparator 或元素實現 Comparable;只能用 poll() 取有序元素。
  5. 穩定排序:TimSort 保證穩定,利用穩定性簡化多鍵排序邏輯。

掌握這套組合拳,任何複雜排序需求都能優雅落地。

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