最近Algorithms 4 課上提到了排序。趁着這個機會,梳理一下。java
Comparable<T>
接口和Comparator<T>
接口都是JDK中提供的和比較相關的接口。使用它們能夠對對象進行比較大小,排序等操做。這算是以後排序的先導知識吧。
Comparable
, 字面意思是「能夠比較的」,因此實現它的類的多個實例應該能夠相互比較「大小」或者「高低」等等。
Comparator
, 字面意思是「比較儀,比較器」, 它應該是專門用來比較用的「工具」。面試
Comparable<T>
接口數組
public interface Comparable<T> { public int compareTo(T o); }
首先看看JDK中怎麼說的:架構
This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's <i>natural ordering</i>, and the class's <tt>compareTo</tt> method is referred to as its <i>natural comparison method</i>.<p>框架
大意是: 任何實現這個接口的類,其多個實例能以固定的次序進行排列。次序具體由接口中的方法compareTo
方法決定。分佈式
Lists (and arrays) of objects that implement this interface can be sorted automatically by {@link Collections#sort(List) Collections.sort} (and {@link Arrays#sort(Object[]) Arrays.sort}).工具
若是某個類實現了這個接口,則它的List
或數組都能使用Collections.sort()
或Arrays.sort()
進行排序。
常見的類如Integer
, Double
, String
都實現了此類。一下子會結合源碼進行分析。源碼分析
咱們先來看Integer
中的實現:性能
public final class Integer extends Number implements Comparable<Integer> { private final int value; public int compareTo(Integer anotherInteger) { return compare(this.value, anotherInteger.value); } public static int compare(int x, int y) { return (x < y) ? -1 : ((x == y) ? 0 : 1); } public static int compareUnsigned(int x, int y) { return compare(x + MIN_VALUE, y + MIN_VALUE); }
咱們只貼出了和比較相關的方法。
能夠看到,compareTo
方法其中調用了compare
方法,這是JDK1.7增長的方法。在Integer
中新增這個方法是爲了減小沒必要要的自動裝箱拆箱。傳入compare
方法的是兩個Integer
的值x
和y
。
若是x < y
, 返回-1
;若是x = y
, 返回0
;若是x > y
, 返回1
。
順便一說,JDK中的實現很是簡潔,只有一行代碼, 當判斷狀況有三種時,使用這種嵌套的判斷 x ? a : b
能夠簡潔很多,這是該學習的。學習
後面的compareUnsigned
是JDK1.8新加入的方法, 用來比較無符號數。這裏的無符號數意思是默認二進制最高位再也不做爲符號位,而是計入數的大小。
其實現是
public static int compareUnsigned(int x, int y) { return compare(x + MIN_VALUE, y + MIN_VALUE); }
直接爲每一個值加了Integer
的最小值 -231。咱們知道Java中int
類型爲4個字節,共32位。符號位佔用一位的話,則其範圍爲-231 到231 - 1。
使用此方法時,全部正數都比負數小。最大值爲 -1
,由於 -1
的二進制全部位均爲 1。
也就是1111 1111 1111 1111 1111 1111 1111 1111
> 其它任何32位數。
具體是什麼狀況呢?
首先咱們知道,在計算機中,全部數都是以二進制存在,也就是0
和1
的組合。
爲了使數字在計算機中運算不出錯,出現了原碼,反碼和補碼。原碼就是一個數的二進制表示,其中最高位爲符號位,表示其正負。
正數的原碼反碼補碼都同樣,負數的反碼是除符號位之外所有取反,補碼爲反碼加1,如圖所示爲32位bits(也就是4比特bytes)數的原碼反碼和補碼。
原碼反碼補碼.jpg
爲何要使用反碼和補碼呢?用四位二進制數舉例:
1
的二進制爲0001
,-1
的二進制爲1001
,若是直接相加,則1 + (-1) = 0
,二進制表示爲0001 + 1001 = 1010 != 0
,因此不能直接使用原碼作運算。
後來出現了反碼,除符號位以外其餘位取反,1001(-1)
取反後爲1110
, 如今作加法 0001 (1) + 1110 (-1) = 1111
。因爲1111
是負數,因此取反以後纔是其真實值,取反後爲1000
,也就是-0
。這能知足條件了,可是美中不足的是,0
帶了負號。惟一的問題其實就出如今0
這個特殊的數值上。 雖然人們理解上+0
和-0
是同樣的, 可是0帶符號是沒有任何意義的。 並且會有0000
原和1000
原兩個編碼表示0
。怎麼辦呢?
人們又想出了補碼,它是反碼加1
。-1
的補碼是 1111
,以上的運算用補碼錶示就是0001 (1) + 1111 (-1) = 0000 = 0
。神奇的發現,這個式子完美契合了十進制加法!
同時咱們留出了1000
,能夠用它表示-8
(-1) + (-7) = (補碼) 1111 + 1001 = 1000 = -8
。注意,因爲此處的-8
使用了以前-0
的補碼來表示,因此-8
沒有沒有原碼和反碼錶示(針對的四位,若是是八位,則沒有原碼和反碼的是-128
,依次類推)。
使用補碼, 不只僅修復了0
的符號以及存在兩個編碼的問題, 並且還可以多表示一個最低數. 這就是爲何4位二進制, 使用原碼或反碼錶示的範圍爲[-7, +7]
, 而使用補碼錶示的範圍爲[-8, 7]
.
二進制加法.jpg
這就是簡單的要用反碼和補碼的緣由。
int
類型在32位系統中佔4個字節、32bit,補碼錶示的的數據範圍爲:
[10000000 00000000 00000000 00000000] ~ [01111111 11111111 11111111 11111111]
[−231,231−1]
[-2147483648, 2147483647]
在java中表示爲:
[Integer.MIN_VALUE, Integer.MAX_VALUE]
與byte
類型的表示同樣,因爲負數比正數多表示了一個數字。對下限去相反數後的數值會超過上限值,溢出到下限,所以下限的相反數與下限相等;對上限去相反數的數值爲負值,該負值比下限的負值大1,在能夠表示的範圍內,所以上限的相反數是上限直接取負值。
看完Integer
後,咱們再來看String
中compareTo
的實現方式:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; // String的值 public int compareTo(String anotherString) { int len1 = value.length; int len2 = anotherString.value.length; int lim = Math.min(len1, len2); // limit, 表示兩個String中長度較小的String長度 char v1[] = value; char v2[] = anotherString.value; int k = 0; while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2; // 若是char不相同,則取其差值 } k++; // 若是char值相同,則繼續日後比較 } return len1 - len2; // 若是全部0 ~ (lim - 1)的char均相同,則比較兩個String的長短 } // 字面意思是對大小寫不敏感的比較器 public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator(); private static class CaseInsensitiveComparator implements Comparator<String>, java.io.Serializable { private static final long serialVersionUID = 8575799808933029326L; public int compare(String s1, String s2) { int n1 = s1.length(); int n2 = s2.length(); int min = Math.min(n1, n2); // 和上面相似,均是取兩個String間的最短長度 for (int i = 0; i < min; i++) { char c1 = s1.charAt(i); char c2 = s2.charAt(i); if (c1 != c2) { c1 = Character.toUpperCase(c1); // 統一換成大寫 c2 = Character.toUpperCase(c2); // 統一換成大寫 if (c1 != c2) { // 大寫若是不相等則再換爲小寫試試 c1 = Character.toLowerCase(c1); c2 = Character.toLowerCase(c2); if (c1 != c2) { // 到此處則肯定不相等 // No overflow because of numeric promotion return c1 - c2; } } } } return n1 - n2; } /** Replaces the de-serialized object. */ private Object readResolve() { return CASE_INSENSITIVE_ORDER; } } // String的方法,能夠直接使用這個方法和其它String進行比較, // 內部實現是調用內部比較器的compare方法 public int compareToIgnoreCase(String str) { return CASE_INSENSITIVE_ORDER.compare(this, str); } }
String
中的關於compare
的方法相對複雜一點,但仍是比較簡單。咱們先不看其餘的代碼,只重點關注compareTo
方法。
public int compareTo(String anotherString) { int len1 = value.length; int len2 = anotherString.value.length; int lim = Math.min(len1, len2); // limit, 表示兩個String中長度較小的String長度 char v1[] = value; char v2[] = anotherString.value; int k = 0; while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2; // 若是char不相同,則取其差值 } k++; // 若是char值相同,則繼續日後比較 } return len1 - len2; // 若是全部0 ~ (lim - 1)的char均相同,則比較兩個String的長短 }
內容很簡潔,就是取兩個String
的長度中較小的,做爲限定值(lim
)。以後對數組下標爲從0
到lim - 1
的char
變量進行遍歷比較,若是遇到不相同的值,返回其差值。通常咱們只用其正負性,若是返回負數則說明第一個對象比第二個對象「小」。
例如比較 "abc"
和"bcd"
,當對各自第一個字符'a'
和 'b'
進行比較時,發現 'a' != 'b'
,則返回 'a' - 'b'
,這個值是負數, char
類型的-1
,Java會自動將其類型強轉爲int
型。最後得出結論"abc"
比"bcd"
小。
Comparator<T>
接口
public interface Comparator<T> { int compare(T o1, T o2); }
這是一個外部排序接口,它的功能是規定「比較大小」的方式。實現它的類能夠做爲參數傳入Collections.sort()
或Arrays.sort()
,使用它的比較方式進行排序。
它能夠爲沒有實現Comparable
接口的類提供排序方式。
String
類中以及Array
類等都有實現此接口的內部類。
在上面String
的源碼中就有一個內部的自定義Comparator
類CaseInsensitiveComparator
, 咱們看看它的源碼。
public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator(); private static class CaseInsensitiveComparator implements Comparator<String>, java.io.Serializable { private static final long serialVersionUID = 8575799808933029326L; public int compare(String s1, String s2) { int n1 = s1.length(); int n2 = s2.length(); int min = Math.min(n1, n2); // 和上面相似,均是取兩個String間的最短長度 for (int i = 0; i < min; i++) { char c1 = s1.charAt(i); char c2 = s2.charAt(i); if (c1 != c2) { c1 = Character.toUpperCase(c1); // 統一換成大寫 c2 = Character.toUpperCase(c2); // 統一換成大寫 if (c1 != c2) { // 大寫若是不相等則再換爲小寫試試 c1 = Character.toLowerCase(c1); c2 = Character.toLowerCase(c2); if (c1 != c2) { // 到此處則肯定不相等 // No overflow because of numeric promotion return c1 - c2; } } } } return n1 - n2; } /** Replaces the de-serialized object. */ private Object readResolve() { return CASE_INSENSITIVE_ORDER; } } // String的方法,能夠直接使用這個方法和其它String進行比較, // 內部實現是調用內部比較器的compare方法 public int compareToIgnoreCase(String str) { return CASE_INSENSITIVE_ORDER.compare(this, str); } }
CaseInsensitiveComparator
, 字面意思是對大小寫不敏感的比較器。
咱們觀察它的compare
方法,能夠發現,它和上面的compareTo
方法實現相似,都是取兩個String
中長度較小的,做爲限定值min
,以後對數組下標爲從0
到min - 1
的char
變量進行遍歷比較。和上面稍有不一樣的是,此處先將char
字符統一換成大寫(upper case), 若是仍然不相等,再將其換爲小寫(lower case)比較。一個字母只有大寫或者小寫兩種情形,若是這兩種狀況都不想等則肯定不相等,返回其差值。若是限定值內全部的char
都相等的話,再去比較兩個String
類型的長度。
例如比較 "abC"
和"ABc"
,compareTo
會直接返回 'a' - 'A'
,而compareToIgnoreCase
方法因爲使用了CaseInsensitiveComparator
,比較結果最終會返回true
。
感興趣能夠加Java架構師羣獲取Java工程化、高性能及分佈式、高性能、深刻淺出。高架構。性能調優、Spring,MyBatis,Netty源碼分析和大數據等多個知識點高級進階乾貨的直播免費學習權限 都是大牛帶飛 讓你少走不少的彎路的 羣..號是:855801563 對了 小白勿進 最好是有開發經驗
注:加羣要求
一、具備工做經驗的,面對目前流行的技術不知從何下手,須要突破技術瓶頸的能夠加。
二、在公司待久了,過得很安逸,但跳槽時面試碰壁。須要在短期內進修、跳槽拿高薪的能夠加。
三、若是沒有工做經驗,但基礎很是紮實,對java工做機制,經常使用設計思想,經常使用java開發框架掌握熟練的,能夠加。
四、以爲本身很牛B,通常需求都能搞定。可是所學的知識點沒有系統化,很難在技術領域繼續突破的能夠加。
5.阿里Java高級大牛直播講解知識點,分享知識,多年工做經驗的梳理和總結,帶着你們全面、科學地創建本身的技術體系和技術認知!