Java的比較器 Comparable 和 Comparator

在 Java 中,ComparableComparator 是用于對象排序的重要接口。它們提供了不同的排序方式,適用于不同的需求,同時在 Java 底層排序算法中發揮著關鍵作用。本文將從基礎概念、使用方法、排序實現(包括升序、降序)、底層實現原理以及適用場景等方面進行詳細解析。


一、?ComparableComparator 的基本概念

在 Java 中,排序通常用于 數組集合(List),兩者的排序分別由 Arrays.sort()Collections.sort() 進行,而這兩個方法都依賴于 ComparableComparator

1.1 Comparable 接口(自然排序)

  • Comparable 是一個 內部比較器,表示對象本身支持排序規則。

  • 需要在類中實現 compareTo() 方法,定義默認的排序方式。

  • 適用于對象有唯一的自然排序方式,如 IntegerStringDouble 等。

代碼示例(按照 age 升序排序):

class Person implements Comparable<Person> {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic int compareTo(Person other) {return Integer.compare(this.age, other.age); // 按年齡升序}@Overridepublic 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` 規則排序System.out.println(Arrays.toString(people));}
}

輸出結果:

[Bob (22), Alice (25), Charlie (30)]

Comparable 的排序方式是 類內部固定的,所有調用 sort() 的地方都使用同樣的規則。


1.2 Comparator 接口(自定義排序)

  • Comparator 是一個 外部比較器,可以用于自定義排序規則。

  • 需要實現 compare() 方法,可以在不同場景使用不同的比較邏輯。

  • 適用于對象有 多種排序需求,如按年齡、姓名、ID 等。

代碼示例(按 name 進行字母升序排序):

class NameComparator implements Comparator<Person> {@Overridepublic 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()); // 使用外部比較器進行排序System.out.println(people);}
}

輸出結果:

[Alice (25), Bob (22), Charlie (30)]

使用 Comparator 可以定義多種排序規則,不同的需求可以使用不同的比較器,非常靈活。


二、升序和降序排序實現

2.1 Comparable 的升序和降序

Comparable 中,只能通過修改 compareTo() 方法來改變排序順序:

@Override
public int compareTo(Person other) {return Integer.compare(other.age, this.age); // 降序排序
}

2.2 Comparator 的升序和降序

使用 Comparator 可以輕松實現 不同排序方式

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 表達式:?

people.sort((p1, p2) -> p1.name.compareTo(p2.name)); // 按姓名排序


3. 底層排序實現

在 Java 中,Arrays.sort()Collections.sort() 在不同數據類型下采用不同的排序算法:

3.1 Arrays.sort()(適用于數組)

  • Arrays.sort() 主要用于 數組排序,其底層實現因數據類型不同而有所不同:

  • 基本類型(int[]double[] 等):使用 Dual-Pivot Quicksort(雙軸快速排序),這是 Quicksort 的一種優化版本。

  • 對象類型(Integer[]String[] 等):使用 TimSort(歸并排序 + 插入排序的優化組合)

3.1.1?基本類型:雙軸快速排序

對于 int[]double[] 等基本數據類型的數組排序,Arrays.sort() 使用的是 雙軸快速排序(Dual-Pivot Quicksort),它是由 Vladimir Yaroslavskiy 在 2009 年提出的改進版 快速排序,其核心思想是:

  1. 選取兩個基準點(pivot),將數組劃分為 三個部分

    • 小于第一個 pivot 的部分

    • 介于兩個 pivot 之間的部分

    • 大于第二個 pivot 的部分

  2. 遞歸對三個子數組進行排序。

這種優化相比于傳統的單軸快速排序,減少了遞歸調用的次數,提高了排序效率。

源碼分析

Arrays.sort(int[] a) 的源碼中:

public static void sort(int[] a) {DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}

它會調用 DualPivotQuicksort.sort(),具體實現如下:

static void sort(int[] a, int left, int right, int[] work, int workBase, int workLen) {if (right - left < QUICKSORT_THRESHOLD) {insertionSort(a, left, right); // 小數組使用插入排序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 主要優化點

  • 雙軸劃分:比傳統快速排序減少遞歸層數,提高效率。

  • 小數據量時使用插入排序,減少不必要的遞歸。

3.1.2對象類型:TimSort(改進版歸并排序)

對于對象數組(如 Integer[]String[]),Java 采用的是 TimSort,它結合了 歸并排序(MergeSort)+ 插入排序(InsertionSort),并做了一些優化:

  1. 數據預處理:TimSort 先尋找 已經排序的子序列(run),如果數據本身有部分有序,它可以減少比較次數。

  2. 小規模數據使用插入排序:避免小規模數據使用歸并排序導致開銷大。

  3. 智能歸并:選擇合適的子序列進行合并,避免不必要的合并操作,提高效率。

源碼分析:
public static <T> void sort(T[] a, Comparator<? super T> c) {if (c == null) {Arrays.sort(a); // 調用默認的 Comparable 方式排序} else {TimSort.sort(a, c); // 使用 Comparator 進行排序}
}

核心代碼:

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); // 小數據量使用插入排序return;}int mid = (lo + hi) >>> 1;sort(a, lo, mid, c);sort(a, mid, hi, c);merge(a, lo, mid, hi, c); // 歸并兩個有序數組
}

TimSort 的優點:

  • 適用于部分有序的數據,比傳統歸并排序更快。

  • 避免不必要的合并,提高效率。


2. Collections.sort() 的底層實現

Collections.sort() 主要用于 List 進行排序,它本質上是 ListArrays.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]);
}

它的執行過程

  1. 將 List 轉換成數組

  2. 調用 Arrays.sort() 進行排序

  3. 再把排好序的數組元素賦值回 List

這意味著 Collections.sort() 的底層仍然是 TimSort

排序方法適用范圍底層實現
Arrays.sort(int[])基本類型數組Dual-Pivot Quicksort(雙軸快速排序)
Arrays.sort(T[])對象數組TimSort(歸并排序 + 插入排序優化)
Collections.sort(List<T>)List 容器TimSort(底層調用 Arrays.sort()
Arrays.sort(arr, Comparator)自定義對象排序TimSort(支持 Comparator

四、結論與總結

  1. Comparable 適用于對象有固定的排序方式,如 StringInteger,實現 compareTo() 進行排序。

  2. Comparator 適用于需要多個排序規則的情況,可以使用 compare() 進行定制排序。

  3. 底層排序算法

    • 基本類型使用 Dual-Pivot QuickSort(雙軸快排)

    • 對象類型和 List 使用 TimSort(歸并排序 + 插入排序優化)

  4. Comparator 更靈活,可以動態傳遞不同的比較器,適用于多種排序需求。

掌握 ComparableComparator,可以幫助你在開發中實現更高效的排序邏輯!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/74179.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/74179.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/74179.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于Qlearning強化學習的太赫茲信道信號檢測與識別matlab仿真

目錄 1.算法仿真效果 2.算法涉及理論知識概要 2.1 太赫茲信道特性 2.2 Q-learning強化學習基礎 2.3 基于Q-learning 的太赫茲信道信號檢測與識別系統 3.MATLAB核心程序 4.完整算法代碼文件獲得 1.算法仿真效果 matlab2024b仿真結果如下&#xff08;完整代碼運行后無水印…

力扣刷題————199.二叉樹的右視圖

給定一個二叉樹的 根節點 root&#xff0c;想象自己站在它的右側&#xff0c;按照從頂部到底部的順序&#xff0c;返回從右側所能看到的節點值。 示例 1&#xff1a; 輸入&#xff1a;root [1,2,3,null,5,null,4] 輸出&#xff1a;[1,3,4] 解題思路&#xff1a;我們可以想到這…

文件包含漏洞的小點總結

文件本地與遠程包含&#xff1a; 文件包含有本地包含與遠程包含的區別&#xff1a;本地包含只能包含服務器已經有的問題&#xff1b; 遠程包含可以包含一切網絡上的文件。 本地包含&#xff1a; ①無限制 感受一下使用phpstudy的文件上傳&#xff0c;開啟phpstudy的apache…

深度學習處理時間序列(5)

Keras中的循環層 上面的NumPy簡單實現對應一個實際的Keras層—SimpleRNN層。不過&#xff0c;二者有一點小區別&#xff1a;SimpleRNN層能夠像其他Keras層一樣處理序列批量&#xff0c;而不是像NumPy示例中的那樣只能處理單個序列。也就是說&#xff0c;它接收形狀為(batch_si…

操作系統相關知識點

操作系統在進行線程切換時需要進行哪些動作&#xff1f; 保存當前線程的上下文 保存寄存器狀態、保存棧信息。 調度器選擇下一個線程 調度算法決策&#xff1a;根據策略&#xff08;如輪轉、優先級、公平共享&#xff09;從就緒隊列選擇目標線程。 處理優先級&#xff1a;實時…

從0到1:Rust 如何用 FFmpeg 和 OpenGL 打造硬核視頻特效

引言&#xff1a;視頻特效開發的痛點&#xff0c;你中了幾個&#xff1f; 視頻特效如今無處不在&#xff1a;短視頻平臺的濾鏡美化、直播間的實時美顏、影視后期的電影級調色&#xff0c;甚至 AI 生成內容的動態效果。無論是個人開發者還是團隊&#xff0c;視頻特效都成了吸引…

【并發編程 | 第一篇】線程相關基礎知識

1.并發和并行有什么區別 并發是指多核CPU上的多任務處理&#xff0c;多個任務在同一時刻真正同時執行。 并行是指單核CPU上的多任務處理&#xff0c;多個任務在同一時間段內交替執行&#xff0c;通過時間片輪轉實現交替執行&#xff0c;用于解決IO密集型瓶頸。 如何理解線程安…

Kafka 偏移量

在 Apache Kafka 中&#xff0c;偏移量&#xff08;Offset&#xff09;是一個非常重要的概念。它不僅用于標識消息的位置&#xff0c;還在多種場景中發揮關鍵作用。本文將詳細介紹 Kafka 偏移量的核心概念及其使用場景。 一、偏移量的核心概念 1. 定義 偏移量是一個非負整數…

18.redis基本操作

Redis(Remote Dictionary Server)是一個開源的、高性能的鍵值對(Key-Value)存儲數據庫,廣泛應用于緩存、消息隊列、實時分析等場景。它以其極高的讀寫速度、豐富的數據結構和靈活的應用方式而受到開發者的青睞。 Redis 的主要特點 ?高性能: ?內存存儲:Redis 將所有數…

歷年跨鏈合約惡意交易詳解(一)——THORChain退款邏輯漏洞

漏洞合約函數 function returnVaultAssets(address router, address payable asgard, Coin[] memory coins, string memory memo) public payable {if (router address(this)){for(uint i 0; i < coins.length; i){_adjustAllowances(asgard, coins[i].asset, coins[i].a…

通俗易懂的講解SpringBean生命周期

&#x1f4d5;我是廖志偉&#xff0c;一名Java開發工程師、《Java項目實戰——深入理解大型互聯網企業通用技術》&#xff08;基礎篇&#xff09;、&#xff08;進階篇&#xff09;、&#xff08;架構篇&#xff09;清華大學出版社簽約作家、Java領域優質創作者、CSDN博客專家、…

深入理解 `git pull --rebase` 與 `--allow-unrelated-histories`:區別、原理與實戰指南

&#x1f680; git pull --rebase vs --allow-unrelated-histories 全面解析 在日常使用 Git 時&#xff0c;我們經常遇到兩種拉取遠程代碼的方式&#xff1a;git pull --rebase 和 git pull --allow-unrelated-histories。它們的區別是什么&#xff1f;各自適用哪些場景&…

Matlab_Simulink中導入CSV數據與仿真實現方法

前言 在Simulink仿真中&#xff0c;常需將外部數據&#xff08;如CSV文件或MATLAB工作空間變量&#xff09;作為輸入信號驅動模型。本文介紹如何高效導入CSV數據至MATLAB工作空間&#xff0c;并通過From Workspace模塊實現數據到Simulink的精確傳輸&#xff0c;適用于運動控制…

Spring Boot 中 JdbcTemplate 處理枚舉類型轉換 和 減少數據庫連接的方法 的詳細說明,包含代碼示例和關鍵要點

以下是 Spring Boot 中 JdbcTemplate 處理枚舉類型轉換 和 減少數據庫連接的方法 的詳細說明&#xff0c;包含代碼示例和關鍵要點&#xff1a; 一、JdbcTemplate 處理枚舉類型轉換 1. 場景說明 假設數據庫存儲的是枚舉的 String 或 int 值&#xff0c;但 Java 實體類使用 enu…

API 安全之認證鑒權

作者&#xff1a;半天 前言 API 作為企業的重要數字資源&#xff0c;在給企業帶來巨大便利的同時也帶來了新的安全問題&#xff0c;一旦被攻擊可能導致數據泄漏重大安全問題&#xff0c;從而給企業的業務發展帶來極大的安全風險。正是在這樣的背景下&#xff0c;OpenAPI 規范…

MATLAB繪圖配色包說明

本欄目將分享MATLAB數據分析圖表&#xff0c;該貼講述配色包的使用 將配色包colormap_nclCM文件夾添加到路徑close all&#xff08;盡量不要刪&#xff09;&#xff0c;使用map colormap(nclCM(309))時會多出來一張空白圖片。配色資源來自slandarer&#xff1b;找不到合適顏色…

Oracle 數據庫系統全面詳解

Oracle 數據庫是全球領先的關系型數據庫管理系統(RDBMS)&#xff0c;由 Oracle 公司開發。它為企業級應用提供了高性能、高可用性、安全性和可擴展性的數據管理解決方案。 目錄 一、Oracle 數據庫體系結構 1. 物理存儲結構 主要組件&#xff1a; 存儲層次&#xff1a; 2. …

Flink介紹——發展歷史

引入 我們整個大數據處理里面的計算模式主要可以分為以下四種&#xff1a; 批量計算&#xff08;batch computing&#xff09; MapReduce Hive Spark Flink pig流式計算&#xff08;stream computing&#xff09; Storm SparkStreaming/StructuredStreaming Flink Samza交互計…

在MFC中使用Qt(四):使用屬性表(Property Sheet)實現自動化Qt編譯流程

前言 首先回顧下前面文章介紹的&#xff1a; 在MFC中使用Qt&#xff08;一&#xff09;&#xff1a;玩膩了MFC&#xff0c;試試在MFC中使用Qt&#xff01;&#xff08;手動配置編譯Qt&#xff09; 在MFC中使用Qt&#xff08;二&#xff09;&#xff1a;實現Qt文件的自動編譯流…

Go紅隊開發— 收官工具

文章目錄 免責聲明個人武器開發美觀輸出Whois查詢反查ip目錄掃描子域名爆破被動掃描主動掃描(字典爆破)CDN檢測 免責聲明 &#x1f4a1; 本博客絕不涉及任何非法用途。 &#x1f4a1; 使用者風險自擔&#xff0c;違規后果自負。 &#x1f4a1; 守法為先&#xff0c;技術向善。 …