Java中比較器Comparable和Comparator超詳細(xì)解析
前言
在 Java 中,Comparable 和 Comparator 是用于對(duì)象排序的重要接口。它們提供了不同的排序方式,適用于不同的需求,同時(shí)在 Java 底層排序算法中發(fā)揮著關(guān)鍵作用。本文將從基礎(chǔ)概念、使用方法、排序?qū)崿F(xiàn)(包括升序、降序)、底層實(shí)現(xiàn)原理以及適用場(chǎng)景等方面進(jìn)行詳細(xì)解析。
一、 Comparable 和 Comparator 的基本概念
在 Java 中,排序通常用于 數(shù)組 和 集合(List),兩者的排序分別由 Arrays.sort() 和 Collections.sort() 進(jìn)行,而這兩個(gè)方法都依賴于 Comparable 和 Comparator。
1.1 Comparable 接口(自然排序)
Comparable是一個(gè) 內(nèi)部比較器,表示對(duì)象本身支持排序規(guī)則。需要在類中實(shí)現(xiàn)
compareTo()方法,定義默認(rèn)的排序方式。適用于對(duì)象有唯一的自然排序方式,如
Integer、String、Double等。
代碼示例(按照 age 升序排序):
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 Integer.compare(this.age, other.age); // 按年齡升序
}
@Override
public String toString() {
return name + " (" + age + ")";
}
}
public class ComparableExample {
public static void main(String[] args) {
Person[] people = {
new Person("Alice", 25),
new Person("Bob", 22),
new Person("Charlie", 30)
};
Arrays.sort(people); // 按 `Comparable` 規(guī)則排序
System.out.println(Arrays.toString(people));
}
}
輸出結(jié)果:
[Bob (22), Alice (25), Charlie (30)]
Comparable 的排序方式是 類內(nèi)部固定的,所有調(diào)用 sort() 的地方都使用同樣的規(guī)則。
1.2 Comparator 接口(自定義排序)
Comparator是一個(gè) 外部比較器,可以用于自定義排序規(guī)則。需要實(shí)現(xiàn)
compare()方法,可以在不同場(chǎng)景使用不同的比較邏輯。適用于對(duì)象有 多種排序需求,如按年齡、姓名、ID 等。
代碼示例(按 name 進(jìn)行字母升序排序):
class NameComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return p1.name.compareTo(p2.name); // 按名稱字母升序
}
}
public class ComparatorExample {
public static void main(String[] args) {
List<Person> people = new ArrayList<>();
people.add(new Person("Alice", 25));
people.add(new Person("Bob", 22));
people.add(new Person("Charlie", 30));
people.sort(new NameComparator()); // 使用外部比較器進(jìn)行排序
System.out.println(people);
}
}
輸出結(jié)果:
[Alice (25), Bob (22), Charlie (30)]
使用 Comparator 可以定義多種排序規(guī)則,不同的需求可以使用不同的比較器,非常靈活。
二、升序和降序排序?qū)崿F(xiàn)
2.1 Comparable 的升序和降序
在 Comparable 中,只能通過修改 compareTo() 方法來改變排序順序:
@Override
public int compareTo(Person other) {
return Integer.compare(other.age, this.age); // 降序排序
}
2.2 Comparator 的升序和降序
使用 Comparator 可以輕松實(shí)現(xiàn) 不同排序方式:
Comparator<Person> ageAscending = Comparator.comparingInt(p -> p.age); // 按年齡升序 Comparator<Person> ageDescending = (p1, p2) -> Integer.compare(p2.age, p1.age); // 按年齡降序
代碼示例:
people.sort(ageAscending); // 升序排序
people.sort(ageDescending); // 降序排序
使用 Java 8 的 Lambda 表達(dá)式:
people.sort((p1, p2) -> p1.name.compareTo(p2.name)); // 按姓名排序
三. 底層排序?qū)崿F(xiàn)
在 Java 中,Arrays.sort() 和 Collections.sort() 在不同數(shù)據(jù)類型下采用不同的排序算法:
3.1 Arrays.sort()(適用于數(shù)組)
Arrays.sort()主要用于 數(shù)組排序,其底層實(shí)現(xiàn)因數(shù)據(jù)類型不同而有所不同:基本類型(
int[]、double[]等):使用 Dual-Pivot Quicksort(雙軸快速排序),這是Quicksort的一種優(yōu)化版本。對(duì)象類型(
Integer[]、String[]等):使用 TimSort(歸并排序 + 插入排序的優(yōu)化組合)。
3.1.1 基本類型:雙軸快速排序
對(duì)于 int[]、double[] 等基本數(shù)據(jù)類型的數(shù)組排序,Arrays.sort() 使用的是 雙軸快速排序(Dual-Pivot Quicksort),它是由 Vladimir Yaroslavskiy 在 2009 年提出的改進(jìn)版 快速排序,其核心思想是:
選取兩個(gè)基準(zhǔn)點(diǎn)(pivot),將數(shù)組劃分為 三個(gè)部分:
小于第一個(gè) pivot 的部分
介于兩個(gè) pivot 之間的部分
大于第二個(gè) pivot 的部分
遞歸對(duì)三個(gè)子數(shù)組進(jìn)行排序。
這種優(yōu)化相比于傳統(tǒng)的單軸快速排序,減少了遞歸調(diào)用的次數(shù),提高了排序效率。
源碼分析
在 Arrays.sort(int[] a) 的源碼中:
public static void sort(int[] a) {
DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}
它會(huì)調(diào)用 DualPivotQuicksort.sort(),具體實(shí)現(xiàn)如下:
static void sort(int[] a, int left, int right, int[] work, int workBase, int workLen) {
if (right - left < QUICKSORT_THRESHOLD) {
insertionSort(a, left, right); // 小數(shù)組使用插入排序
return;
}
int pivot1 = a[left], pivot2 = a[right];
if (pivot1 > pivot2) {
swap(a, left, right);
pivot1 = a[left];
pivot2 = a[right];
}
int less = left + 1;
int great = right - 1;
for (int k = less; k <= great; k++) {
if (a[k] < pivot1) {
swap(a, k, less++);
} else if (a[k] > pivot2) {
swap(a, k, great--);
}
}
sort(a, left, less - 1, work, workBase, workLen);
sort(a, less, great, work, workBase, workLen);
sort(a, great + 1, right, work, workBase, workLen);
}
可以看出,Dual-Pivot Quicksort 主要優(yōu)化點(diǎn):
雙軸劃分:比傳統(tǒng)快速排序減少遞歸層數(shù),提高效率。
小數(shù)據(jù)量時(shí)使用插入排序,減少不必要的遞歸。
3.1.2對(duì)象類型:TimSort(改進(jìn)版歸并排序)
對(duì)于對(duì)象數(shù)組(如 Integer[]、String[]),Java 采用的是 TimSort,它結(jié)合了 歸并排序(MergeSort)+ 插入排序(InsertionSort),并做了一些優(yōu)化:
數(shù)據(jù)預(yù)處理:TimSort 先尋找 已經(jīng)排序的子序列(run),如果數(shù)據(jù)本身有部分有序,它可以減少比較次數(shù)。
小規(guī)模數(shù)據(jù)使用插入排序:避免小規(guī)模數(shù)據(jù)使用歸并排序?qū)е麻_銷大。
智能歸并:選擇合適的子序列進(jìn)行合并,避免不必要的合并操作,提高效率。
源碼分析:
public static <T> void sort(T[] a, Comparator<? super T> c) {
if (c == null) {
Arrays.sort(a); // 調(diào)用默認(rèn)的 Comparable 方式排序
} else {
TimSort.sort(a, c); // 使用 Comparator 進(jìn)行排序
}
}
核心代碼:
static <T> void sort(T[] a, Comparator<? super T> c) {
int lo = 0, hi = a.length;
if (hi - lo < INSERTION_SORT_THRESHOLD) {
insertionSort(a, lo, hi, c); // 小數(shù)據(jù)量使用插入排序
return;
}
int mid = (lo + hi) >>> 1;
sort(a, lo, mid, c);
sort(a, mid, hi, c);
merge(a, lo, mid, hi, c); // 歸并兩個(gè)有序數(shù)組
}
TimSort 的優(yōu)點(diǎn):
適用于部分有序的數(shù)據(jù),比傳統(tǒng)歸并排序更快。
避免不必要的合并,提高效率。
3.2. Collections.sort() 的底層實(shí)現(xiàn)
Collections.sort() 主要用于 List 進(jìn)行排序,它本質(zhì)上是 List 的 Arrays.sort(),所以它的底層也是 TimSort。
public static <T extends Comparable<? super T>> void sort(List<T> list) {
Object[] array = list.toArray();
Arrays.sort(array);
for (int i = 0; i < list.size(); i++)
list.set(i, (T) array[i]);
}
它的執(zhí)行過程:
將 List 轉(zhuǎn)換成數(shù)組
調(diào)用 Arrays.sort() 進(jìn)行排序
再把排好序的數(shù)組元素賦值回 List
這意味著 Collections.sort() 的底層仍然是 TimSort。
| 排序方法 | 適用范圍 | 底層實(shí)現(xiàn) |
|---|---|---|
Arrays.sort(int[]) | 基本類型數(shù)組 | Dual-Pivot Quicksort(雙軸快速排序) |
Arrays.sort(T[]) | 對(duì)象數(shù)組 | TimSort(歸并排序 + 插入排序優(yōu)化) |
Collections.sort(List<T>) | List 容器 | TimSort(底層調(diào)用 Arrays.sort()) |
Arrays.sort(arr, Comparator) | 自定義對(duì)象排序 | TimSort(支持 Comparator) |
四、結(jié)論與總結(jié)
Comparable 適用于對(duì)象有固定的排序方式,如
String、Integer,實(shí)現(xiàn)compareTo()進(jìn)行排序。Comparator 適用于需要多個(gè)排序規(guī)則的情況,可以使用
compare()進(jìn)行定制排序。底層排序算法:
基本類型使用 Dual-Pivot QuickSort(雙軸快排)。
對(duì)象類型和 List 使用 TimSort(歸并排序 + 插入排序優(yōu)化)。
Comparator 更靈活,可以動(dòng)態(tài)傳遞不同的比較器,適用于多種排序需求。
掌握 Comparable 和 Comparator,可以幫助你在開發(fā)中實(shí)現(xiàn)更高效的排序邏輯!
到此這篇關(guān)于Java中比較器Comparable和Comparator的文章就介紹到這了,更多相關(guān)Java比較器Comparable和Comparator內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于maven打包時(shí)的報(bào)錯(cuò): Return code is: 501 , ReasonPhrase:HTTPS Requ
這篇文章主要介紹了關(guān)于maven打包時(shí)的報(bào)錯(cuò): Return code is: 501 , ReasonPhrase:HTTPS Required,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
Java線程的并發(fā)工具類實(shí)現(xiàn)原理解析
本文給大家講解Java線程的并發(fā)工具類的一些知識(shí),通過適用場(chǎng)景分析大數(shù)據(jù)量統(tǒng)計(jì)類任務(wù)的實(shí)現(xiàn)原理和封裝,多個(gè)示例代碼講解的非常詳細(xì),對(duì)java線程并發(fā)工具類相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)下吧2021-06-06
SpringCloud gateway request的body驗(yàn)證或修改方式
這篇文章主要介紹了SpringCloud gateway request的body驗(yàn)證或修改方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
SpringBoot利用jackson格式化時(shí)間的三種方法
日常開發(fā)過程中經(jīng)常會(huì)使用json進(jìn)行數(shù)據(jù)的傳輸,這就涉及到了對(duì)象和json的相互轉(zhuǎn)化,常用的解決方案有:Jackson(推薦)、谷歌的Gson、阿里的Fastjson,這篇文章主要給大家介紹了關(guān)于SpringBoot如何利用jackson格式化時(shí)間的相關(guān)資料,需要的朋友可以參考下2021-06-06
SpringBoot項(xiàng)目中filter的兩種使用詳解
文章介紹了兩種方式在Spring Boot中創(chuàng)建和配置過濾器:使用`@WebFilter`注解和`@ServletComponentScan`注解,以及通過`@Bean`注解自定義過濾器并加載到IOC容器,通過測(cè)試啟動(dòng)項(xiàng)目并訪問指定URL,可以查看控制臺(tái)打印的過濾器執(zhí)行順序結(jié)果2025-10-10
Spring中的Eureka服務(wù)過期詳細(xì)解析
這篇文章主要介紹了Spring中的Eureka服務(wù)過期詳細(xì)解析,如果有一些服務(wù)過期了,或者宕機(jī)了,就不會(huì)調(diào)用shutdown()方法,也不會(huì)去發(fā)送請(qǐng)求下線服務(wù)實(shí)例,需要的朋友可以參考下2023-11-11
Android中比較常見的Java super關(guān)鍵字
這篇文章主要為大家介紹了Android中比較常見的Java super關(guān)鍵字,具有一定的學(xué)習(xí)參考價(jià)值,感興趣的小伙伴們可以參考一下2016-01-01

