對 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 + ")"; }
}
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)]
// 匿名內部類(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()); // 降序
// 靜態常量,全局復用
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);
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 降序
// 先按名稱升序,名稱相同再按 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 內部為最小堆,出隊順序即排序順序。三種定製方式:
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()); // 頻率高先出
PriorityQueue<Term> pq = new PriorityQueue<>(Comparator.comparingInt(Term::getId));
PriorityQueue<Term> pq = new PriorityQueue<>((t1, t2) -> t1.id - t2.id);
// ❌ 錯誤:for-each 不保證順序,內部數組存儲順序 ≠ 堆序
for (WordFreq wf : pq) System.out.println(wf);
// ✅ 正確:poll() 依次取出堆頂
while (!pq.isEmpty()) System.out.println(pq.poll());
String、Integer、Date 等已實現 Comparable,直接 list.sort(null) 或 Collections.sort(list) 即可自然順序。| 場景 | 建議 |
|---|---|
| 大列表(>10萬) | 優先 list.sort(comparator)(原地排序),避免 Stream.sorted() 產生新流 |
| 頻繁排序同一列表 | 復用 static final Comparator,避免重複創建 Lambda 實例 |
| 基本類型排序 | 用 Comparator.comparingInt/Long/Double 避免裝箱 |
| 並行排序 | JDK 8+ Arrays.parallelSort()(陣列),List 需先 toArray |
| 自定義 compareTo 邏輯錯誤 | 必須滿足:自反性、對稱性、傳遞性;違反會導致排序異常或死循環 |
Comparable,list.sort(null)。list.sort(Comparator.comparing(...))(Lambda/方法引用首選)。static final Comparator 常量。掌握這套組合拳,任何複雜排序需求都能優雅落地。