Java對象排序、中文排序、SortedSet排序使用和源碼講解

来源:http://www.cnblogs.com/8hao/archive/2016/03/04/5241941.html
-Advertisement-
Play Games

原文出處: xieyu_zy 在C、C++中有很多排序演算法,但是通常排序演算法不得不讓程式員在寫代碼的過程中陷入對底層很多指針和位置的理解,java不希望這樣,所以排序大多可以由java幫你做掉,例如,你要對一個數組排序,就通過:Collections.sort(list)那麼這個list就被排序了,


原文出處: xieyu_zy

在C、C++中有很多排序演算法,但是通常排序演算法不得不讓程式員在寫代碼的過程中陷入對底層很多指針和位置的理解,java不希望這樣,所以排序大多可以由java幫你做掉,例如,你要對一個數組排序,就通過:Collections.sort(list)那麼這個list就被排序了,排序最終調用的是Arrays.sort方法來完成的,所以數組自然是用Arrays.sort了,而SortedSet裡面內部也有排序功能也是類似的方式的來實現的,只是內部調用了相關的方法來完成而已;SortedSet只是一個介面,實現類有很多,本文以TreeSet實現類作為例子。

而排序必然就存在對比大小,那麼傳遞的信息,java是通過什麼來對比大小的呢?compareTo這個來對比的,而內部對比過程中,需要將數據轉換為Comparable來對比,所以你的對象就需要implementsComparable,並實現內部的方法compareTo,只要你的compareTo實現是你所想要的,那麼排序必然是正確的,那麼是否還有其他的方法,有的,排序的時候,允許你傳入一個對比類,因為這樣也可以減少一些空指針出現的可能性,傳入的類需要實現:Comparator介面,實現其方法:compare類,雖然介面中還定義了equals方法基本不用管它,因為Object就已經實現了,並且內部排序中並沒有用到equals方法來做排序。

下麵開始使用實例分別來做中文排序、對象排序,並分別使用對象實現Comparable介面,以及單獨定義排序對象實現Comparator介面來完成排序:

實例1(通過實現Comparator介面完成中文排序):

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import java.text.Collator; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List;   public class ChineseSortCompare {       @SuppressWarnings("rawtypes")     private final static Comparator CHINA_COMPARE = Collator.getInstance(java.util.Locale.CHINA);       public static void main(String []args) {         sortArray();         sortList();         System.out.println("李四".compareTo("張三"));//前者大於後者,則為正數,否則為負數,相等為0     }       @SuppressWarnings("unchecked")     private static void sortList() {         List<String>list = Arrays.asList("張三", "李四", "王五");         Collections.sort(list , CHINA_COMPARE);         for(String str : list) {             System.out.println(str);         }     }       @SuppressWarnings("unchecked")     private static void sortArray() {         String[] arr = {"張三", "李四", "王五"};         Arrays.sort(arr, CHINA_COMPARE);         for(String str : arr) {             System.out.println(str);         }     } }

可以看到輸出的結果都是一樣的,當然String本身有compare方法,而且其本身也是實現了Comparable介面的,所以你如果不放入CHINA_COMPARE來進行處理的話,將會預設按照String自己的compareTo來做排序,排序的結果自然不是你想要的,當然英文應該是你想要的。

實例2(通過外部定義Comparator來完成對象排序):

這裡首先要構造一個對象的類,為了簡單,我們就用兩屬性,定義一個UserDO這樣一個類,描述如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class UserDO {       protected String name;       protected String email;       public UserDO() {}       public UserDO(String name , String email) {         this.name = name;         this.email = email;     }       public String getName() {         return name;     }       public void setName(String name) {         this.name = name;     }       public String getEmail() {         return email;     }       public void setEmail(String email) {         this.email = email;     } }

定義了兩個屬性為name和email,此時我們想要按照name了排序,那麼我們定義排序的類如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 import java.text.Collator; import java.util.Comparator;   public class UserDOComparator implements Comparator<UserDO> {       Collator cmp = Collator.getInstance(java.util.Locale.CHINA);       @Override     public int compare(UserDO userDO1, UserDO userDO2) {           return cmp.compare(userDO1.getName(), userDO2.getName());     } }

此時可以看出我們實現了compare方法,是使用拼音排序的,然後我們來模擬一些數據驗證結果:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.SortedSet; import java.util.TreeSet;   public class SortUserListTest {       private final static UserDOComparator USER_COMPARATOR = new UserDOComparator();       public static void main(String []args) {         sortUserDOArray();         sortUserDOList();         sortUserBySortedSet();     }       private static void sortUserBySortedSet() {         SortedSet<UserDO>userSet = new TreeSet<UserDO>(USER_COMPARATOR);         userSet.add(new UserDO("張三" , "[email protected]"));         userSet.add(new UserDO("李四" , "[email protected]"));         userSet.add(new UserDO("王五" , "[email protected]"));         for(UserDO userDO : userSet) {             System.out.println(userDO.getName());         }     }       private static void sortUserDOList() {         List<UserDO>list = Arrays.asList(                 new UserDO("張三" , "[email protected]"),                 new UserDO("李四" , "[email protected]"),                 new UserDO("王五" , "[email protected]")         );         Collections.sort(list , USER_COMPARATOR);         for(UserDO userDO : list) {             System.out.println(userDO.getName());         }     }       private static void sortUserDOArray() {         UserDO []userDOArray = new UserDO[] {             new UserDO("張三" , "[email protected]"),             new UserDO("李四" , "[email protected]"),             new UserDO("王五" , "[email protected]")         };         Arrays.sort(userDOArray , USER_COMPARATOR);         for(UserDO userDO : userDOArray) {             System.out.println(userDO.getName());         }     } }

根據這些輸入,你可以看到它的輸出和實際想要的按照名稱的拼音排序是一致的,那麼有人會問,如果我按照兩個欄位排序,先按照一個欄位排序,再按照另一個欄位排序該怎麼辦,其次如果是倒敘應該是如何操作,其實倒敘來講只需要在compare方法中將原有的輸出改成相反數就可以了,compare得到的結果為正數、負數、或0,若為正數,代表第一個數據比第二個大,而負數相反,為0的時候代表相等;而多欄位排序也是如此,通過第一層排序後得到結果,看是否是0,如果是0,那麼就再按照第二個欄位排序即可,否則就直接返回第一層返回的結果,兩者混合應用以及多層排序自然就實現了。

實例3(將上面的UserDO使用一個叫UserComparableDO在類的基礎上進行排序)

首先將UserDO重新編寫為UserComparableDO:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import java.text.Collator; import java.util.Comparator;   public class UserComparableDO extends UserDO implements Comparable<UserDO> {       public UserComparableDO() {}       public UserComparableDO(String name , String email) {         this.name = name;         this.email = email;     }       @SuppressWarnings("rawtypes")     private final static Comparator CHINA_COMPARE = Collator.getInstance(java.util.Locale.CHINA);       @SuppressWarnings("unchecked")     @Override     public int compareTo(UserDO userDO) {         return CHINA_COMPARE.compare(this.getName(), userDO.getName());     } }

當然這段代碼裡面直接在裡面定義一個Comparator是不正確的,一般這個東西是被抽象到系統某些公共的Commons組件裡面的,其次,如果原本沒有UserDO類,相應的屬性寫一次即可,我這裡原本有UserDO所有直接集成,減少很多代碼。

此時就不需要自己再去寫一個Comparator了,就可以直接排序了,下麵是我們的測試程式:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.SortedSet; import java.util.TreeSet;   public class SortUserListByComparable {       public static void main(String []args) {         sortUserBySortedSet();         sortUserDOList();         sortUserDOArray();     }       private static void sortUserBySortedSet() {         SortedSet<UserComparableDO>userSet = new TreeSet<UserComparableDO>();         userSet.add(new UserComparableDO("張三" , "[email protected]"));         userSet.add(new UserComparableDO("李四" , "[email protected]"));         userSet.add(new UserComparableDO("王五" , "[email protected]"));         for(UserComparableDO userDO : userSet) {             System.out.println(userDO.getName());         }     }       private static void sortUserDOList() {         List<UserComparableDO>list = Arrays.asList(                 new UserComparableDO("張三" , "[email protected]"),                 new UserComparableDO("李四" , "[email protected]"),                 new UserComparableDO("王五" , "[email protected]")         );         Collections.sort(list);         for(UserComparableDO userDO : list) {             System.out.println(userDO.getName());         }     }       private static void sortUserDOArray() {         UserComparableDO []userDOArray = new UserComparableDO[] {             new UserComparableDO("張三" , "[email protected]"),             new UserComparableDO("李四" , "[email protected]"),             new UserComparableDO("王五" , "[email protected]")         };         Arrays.sort(userDOArray);         for(UserComparableDO userDO : userDOArray) {             System.out.println(userDO.getName());         }     } }

可以看到本次排序中沒有再使用自定義的Comparator作為參數,另外TreeSet的入口參數也沒有再傳入這些參數。

結果知道了,我們簡單看看相關的源碼來證實這個說法,我們首先來看Collections.sort方法:

源碼片段1:Collections.sort(List<T> list)

1 2 3 4 5 6 7 8 9 public static <T extends Comparable<? super T>> void sort(List<T> list) {     Object[] a = list.toArray();     Arrays.sort(a);     ListIterator<T> i = list.listIterator();     for (int j=0; j<a.length; j++) {         i.next();         i.set((T)a[j]);     } }

此時直接調用了Arrays.sort(a)來排序後,將數組的數據寫回到list,另外根據方法的定義,泛型T要求傳入的類必須是Comparable類的子類或實現類,所以要調用Collections.sort(list)這個方法,傳入的list中包含的每行數據必須是implements Comparable這個介面的,否則編譯時就會報錯。

再看重載方法,傳入自定義的Comparator

源碼片段2:Collections.sort(List<T> list, Comparator<? super T> c)

1 2 3 4 5 6 7 8 9 public static <T> void sort(List<T> list, Comparator<? super T> c) {     Object[] a = list.toArray();     Arrays.sort(a, (Comparator)c);     ListIterator i = list.listIterator();     for (int j=0; j<a.length; j++) {         i.next();         i.set(a[j]);     } }

也是和第一個方法類似,就是調用了Arrays.sort相應的重載方法,看來都是在Arrays裡面是實現的,那麼就進一步向下看:

源碼片段3:Arrays.sort(T[]t):

1 2 3 4 public static void sort(Object[] a) {         Object[] aux = (Object[])a.clone();         mergeSort(aux, a, 0, a.length, 0); }

看來代碼片段交給了mergeSort來處理,而對數組做了一次克隆,作為排序的基礎數據,而原來的數組作為排序的目標,mergeSort的代碼片段應該是核心部分,我們先放在這裡,先看下sort的另一個重載方法,另外需要註意,這裡並沒有像Collections.sort(List<T>list)那樣在編譯時檢查類型,也就是在使用這個方法的時候,數組裡面的每行並沒有implements Comparable也會不會出錯,只是在運行時會報錯而已,在下麵的源碼中會有說明。

源碼片段4 : Arrays.sort(T[]t, Comparator<? super T> c)

1 2 3 4 5 6 7 8 public static <T> void sort(T[] a, Comparator<? super T> c) {     T[] aux = (T[])a.clone();         if (c==null)             mergeSort(aux, a, 0, a.length, 0);         else             mergeSort(aux, a, 0, a.length, 0, c); }

看來mergeSort也進行了重載,也就是當傳入了自定義的Comparator和不傳入自定義的Comparator是調用不同的方法來實現的,然後我們來看下兩個方法的實現。

源碼片段5:mergeSort(Object[]src , Object[]dst , int low , int high , int off)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • http://www.ourd3js.com/wordpress/
  • Lua中的協程和多線程很相似,每一個協程有自己的堆棧,自己的局部變數,可以通過yield-resume實現在協程間的切換。不同之處是:Lua協程是非搶占式的多線程,必須手動在不同的協程間切換,且同一時刻只能有一個協程在運行。並且Lua中的協程無法在外部將其停止,而且有可能導致程式阻塞。 協同程式(C
  • 2014年3月,Java 8發佈,Lambda表達式作為一項重要的特性隨之而來。或許現在你已經在使用Lambda表達式來書寫簡潔靈活的代碼。比如,你可以使用Lambda表達式和新增的流相關的API,完成如下的大量數據的查詢處理: int total = invoices.stream() .filt
  • Queue介面與List、Set同一級別,都是繼承了Collection介面。LinkedList實現了Queue接 口。Queue介面窄化了對LinkedList的方法的訪問許可權(即在方法中的參數類型如果是Queue時,就完全只能訪問Queue介面所定義的方法 了,而不能直接訪問 LinkedLi
  • 在 openssl 中使用非阻塞的bio方法紀要。通過與 epoll 的配合,完成高效的加密連接處理。
  • 本文內容來源:http://blog.privatenode.in/torifying-scrapy-project-on-ubuntu/ 在使用Scrapy的時候,一旦進行高頻率的爬取就容易被封IP,此時可以通過使用TOR來進行匿名爬取,同時要安裝Polipo代理伺服器 註意:要進行下麵的操作的前...
  • python web 部署 web開發中,各種語言爭奇鬥艷,web的部署方面,卻沒有太多的方式。簡單而已,大概都是 nginx 做前端代理,中間 webservice 調用程式腳本。大概方式:nginx + webservice + script nginx 不用多說,一個高性能的web伺服器。通常
  • 一、需求背景 1. 需求:spring MVC框架controller間跳轉,需重定向。有幾種情況:不帶參數跳轉,帶參數拼接url形式跳轉,帶參數不拼接參數跳轉,頁面也能顯示。 @RequestMapping(value = "/activityType", method = RequestMeth
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...