接口Comparator 1.1
前面我們講過Java提供了一個用於比較的接口Comparable,提供了一個比較的方法,所有實現該接口的類,都動態的實現了該比較方法。實際上Java中除了比較一個接口外,還提供了一個接口,該接口也是具有比較的功能,但該接口注重的卻是比較容器,然后對其排序,這就是Comparator,下面我們就來具體認識一下;
1. 接口概述
Comparator並不想Comparable那樣有許許多多的實現類,該接口的實現類只有兩個Collator, RuleBasedCollator,但是該接口提供的兩個方法,仍然是十分有用的,下面我們就來詳細通過API介紹一下接口,下節介紹接口方法;
查看API:
public interface Comparator<T>
強行對某個對象 collection 進行整體排序的比較函數。可以將 Comparator 傳遞給 sort 方法(如 Collections.sort 或 Arrays.sort),從而允許在排序順序上實現精確控制。還可以使用 Comparator 來控制某些數據結構(如有序 set或有序映射)的順序,或者為那些沒有自然順序的對象 collection 提供排序。
當且僅當對於一組元素 S 中的每個e1和e2 而言,c.compare(e1, e2)==0與 e1.equals(e2) 具有相等的布爾值時,Comparator c 強行對 S 進行的排序才叫做與 equals 一致的排序。
當使用具有與 equals 不一致的強行排序能力的 Comparator 對有序 set(或有序映射)進行排序時,應該小心謹慎。假定一個帶顯式 Comparator c 的有序 set(或有序映射)與從 set S 中抽取出來的元素(或鍵)一起使用。如果 c 強行對 S 進行的排序是與 equals 不一致的,那么有序 set(或有序映射)將是行為“怪異的”。尤其是有序 set(或有序映射)將違背根據 equals 所定義的 set(或映射)的常規協定。 例如,假定使用 Comparator c 將滿足 (a.equals(b) && c.compare(a, b) != 0) 的兩個元素 a 和 b 添加到一個空 TreeSet 中,則第二個 add 操作將返回 true(樹 set 的大小將會增加),因為從樹 set 的角度來看,a 和 b 是不相等的,即使這與 Set.add 方法的規范相反。
注:通常來說,讓 Comparator 也實現 java.io.Serializable 是一個好主意,因為它們在可序列化的數據結構(像 TreeSet、TreeMap)中可用作排序方法。為了成功地序列化數據結構,Comparator(如果已提供)必須實現 Serializable。
在算術上,定義給定 Comparator c 對給定對象 set S 實施強行排序 的關系式 為:
{(x, y) such that c.compare(x, y) <= 0}.
此整體排序的商 (quotient) 為:
{(x, y) such that c.compare(x, y) == 0}.
它直接遵循 compare 的協定,商是 S 上的等價關系,強行排序是 S 上的整體排序。當我們說 c 強行對 S 的排序是與 equals 一致 的時,意思是說排序的商是對象的 equals(Object) 方法所定義的等價關系:
{(x, y) such that x.equals(y)}.
此接口是 Java Collections Framework 的成員。
2. 接口方法詳解
作為接口Comparator提供了兩個抽象方法,如下:
int |
|
boolean |
通過API,我們來解讀接口方法:
Compare()比較用來排序的兩個參數。根據第一個參數小於、等於或大於第二個參數分別返回負整數、零或正整數。 在前面的描述中,符號 sgn(expression) 表示 signum 數學函數,根據 expression 的值為負數、0 還是正數,該函數分別返回 -1、0 或 1。
- 實現程序必須確保對於所有的 x 和 y 而言,都存在 sgn(compare(x, y)) == -sgn(compare(y, x))。(這意味着當且僅當 compare(y, x) 拋出異常時 compare(x, y) 才必須拋出異常。)
- 實現程序還必須確保關系是可傳遞的:((compare(x, y)>0) && (compare(y, z)>0)) 意味着 compare(x, z)>0。
- 最后,實現程序必須確保 compare(x, y)==0 意味着對於所有的 z 而言,都存在 sgn(compare(x, z))==sgn(compare(y, z))。
雖然這種情況很普遍,但並不 嚴格要求 (compare(x, y)==0) == (x.equals(y))。一般說來,任何違背這個條件的 Comparator 都應該清楚地指出這一事實。推薦的語言是“注意:此 Comparator 強行進行與 equals 不一致的排序。”
可能拋出異常ClassCastException - 如果參數的類型不允許此 Comparator 對它們進行比較。
Equals()指示某個其他對象是否“等於”此 Comparator。此方法必須遵守 Object.equals(Object) 的常規協定。此外,僅當 指定的對象也是一個 Comparator,並且強行實施與此 Comparator 相同的排序時,此方法才返回 true。因此,comp1.equals(comp2) 意味着對於每個對象引用 o1 和 o2 而言,都存在 sgn(comp1.compare(o1, o2))==sgn(comp2.compare(o1, o2))。
注意,不重寫 Object.equals(Object) 方法總是安全的。然而,在某些情況下,重寫此方法可以允許程序確定兩個不同的 Comparator 是否強行實施了相同的排序,從而提高性能。
覆蓋: 類 Object 中的 equals
參數: obj - 要進行比較的引用對象。
返回: 僅當指定的對象也是一個 Comparator,並且強行實施與此 Comparator 相同的排序時才返回 true。
3. 接口方法實踐操作
上面我們對接口中的兩個方法進行了一番概述,那么接下來我們就來實踐操作一下這兩個方法。
package cn.comparator.A; import java.util.Comparator; /** * 我們考慮這樣一種情況,那么就是一個String類型的數組,我們希望數組的排序是 * 按照字符串的長度來排序,而不是字典書序,那么此時顯然String的compareTo不適用了, * 並且String是final類,也不允許我們更改,那么此時該怎么做呢? * Arrays提供了一個用於排序的sort方法,如果不傳遞比較器的話, * 那么默認使用該類型的比較器進行比較,這里要比較的是String,String的比較器不符合我們 * 的需求,那么最好的做法就是自己定義個比較器,然后傳遞進去,下面來試一下 */ public class ComparatorLength implements Comparator<String>{ /** * 如果參數1的長度大於參數2的長度,那么返回1,否則返回-1 */ @Override public int compare(String o1, String o2) { int len1 = o1.length(); int len2 = o2.length(); return (len1 - len2 > 0) ? 1 : -1; } }
@Test public void test2(){ String[] str = {"aadsaf","dqwd","dqwfcsqc","xqsccaac","csfwffqaf","czxca","cas","cacs","casc"}; Comparator com1 = new ComparatorLength(); Comparator com2 = new ComparatorLength(); Arrays.sort(str, com1); System.out.println(Arrays.toString(str)); Arrays.sort(str, com2); System.out.println(Arrays.toString(str)); System.out.println((com1 == com2) +"--"+ (com1.equals(com2))); }
上面我們簡單對Comparator的方法進行了實踐,那么我們考慮對數組進行更詳細的排序,按照字符排序,並且重寫equals方法,注意equals方法是重寫Object的,Comparator雖然具有equals,但是使用Eclipse快捷重寫該方法時,沒有任何提示,下面我們來看一下:
package cn.comparator.A; import java.util.Comparator; public class ComparatorLeng implements Comparator<String>{ /** * 比較字符串元素 */ @Override public int compare(String o1, String o2) { return o1.compareTo(o2); } @Override public boolean equals(Object o) { Comparator com = (Comparator) o; return this.compare(o1, o2) == com.compare(o1, o2); } }
@Test public void test3(){ String[] str = {"aadsaf","dqwd","dqwfcsqc","xqsccaac","csfwffqaf","czxca","cas","cacs","casc"}; Comparator com1 = new ComparatorLeng(); Comparator com2 = new ComparatorLeng(); Arrays.sort(str, com1); System.out.println(Arrays.toString(str)); Arrays.sort(str, com2); System.out.println(Arrays.toString(str)); System.out.println((com1 == com2) +"--"+ (com1.equals(com2))); }
我們看到實現了既定操作,我們考慮一下,我們是學習過排序算法的,那么試驗一下:
package cn.comparator.A; import java.util.Comparator; public class ComparatorLeng implements Comparator<String>{ String o1 = "sacWDVWdvWDSCDXCsfsdf"; String o2 = "FWEFxsafcsdavxFWEFVcss"; /** * 比較字符串元素 */ @Override public int compare(String o1, String o2) { return o1.compareTo(o2); } @Override public boolean equals(Object o) { Comparator com = (Comparator) o; return this.compare(o1, o2) == com.compare(o1, o2); } public Comparable[] sort(Comparable<String>[] arr){ //將數組按照升序排列 int len = arr.length; for (int i = 0; i < len; i++) { //將arr[i]插入到arr[i-1],arr[i-2]....之中去 for (int j = i; j > 0 && less(arr[j], arr[j-1]); j--) { //交換arr[j]和arr[j-1]元素 exch(arr, j, j-1); } } return arr; } //比較數組元素 public boolean less(Comparable c1,Comparable c2){ return c1.compareTo(c2) < 0; } //交換元素 public void exch(Comparable[] com, int i,int j){ Comparable ce = com[i]; com[i] = com[j]; com[j] = ce; } }
@Test public void test4(){ String[] str = {"aadsaf","dqwd","dqwfcsqc","xqsccaac","csfwffqaf","czxca","cas","cacs","casc"}; String[] str1 = {"aadsaf","dqwd","dqwfcsqc","xqsccaac","csfwffqaf","czxca","cas","cacs","casc"}; ComparatorLeng com1 = new ComparatorLeng(); str = (String[]) com1.sort(str); System.out.println(Arrays.toString(str)); Arrays.sort(str1, com1); System.out.println(Arrays.toString(str1)); }
綜上,我們簡單了解了Comparator的方法,我們會在以后的學習中進行加強學習,這個是后話了;