1. 程式人生 > >Comparable和Comparator以及Arrays.sort方法

Comparable和Comparator以及Arrays.sort方法

 Comparable & Comparator 都是用來實現集合中元素的比較、排序的,只是 Comparable 是在集合內部定義的方法實現的排序,Comparator 是在集合外部實現的排序,所以,如想實現排序,就需要在集合外定義 Comparator 介面的方法或在集合內實現 Comparable 介面的方法。

Comparator位於包java.util下,而Comparable位於包   java.lang下

Comparable 是一個物件本身就已經支援自比較所需要實現的介面(如 String、Integer 自己就可以完成比較大小操作,已經實現了Comparable介面)   


 自定義的類要在加入list容器中後能夠排序,可以實現Comparable介面,在用Collections類的sort方法排序時,如果不指定Comparator,那麼就以自然順序排序,如API所說:
Sorts the specified list into ascending order, according to the natural ordering of its elements. All elements in the list must implement the Comparable interface
這裡的自然順序就是實現Comparable介面設定的排序方式。

而 Comparator 是一個專用的比較器,當這個物件不支援自比較或者自比較函式不能滿足你的要求時,你可以寫一個比較器來完成兩個物件之間大小的比較。


可以說一個是自已完成比較,一個是外部程式實現比較的差別而已。

用 Comparator 是策略模式(strategy design pattern),就是不改變物件自身,而用一個策略物件(strategy object)來改變它的行為。

比如:你想對整數採用絕對值大小來排序,Integer 是不符合要求的,你不需要去修改 Integer 類(實際上你也不能這麼做)去改變它的排序行為,只要使用一個實現了 Comparator 介面的物件來實現控制它的排序就行了。

Collections.sort((List<T> list, Comparator<? super T> c)是用來對list排序

的。


如果不是呼叫sort方法,相要直接比較兩個物件的大小,如下:
Comparator定義了倆個方法,分別是   int   compare(T   o1,   T   o2)和   boolean   equals(Object   obj),
用於比較兩個Comparator是否相等
true only if the specified object is also a comparator and it imposes the same ordering as this comparator.
有時在實現Comparator介面時,並沒有實現equals方法,可程式並沒有報錯,原因是實現該介面的類也是Object類的子類,而Object類已經實現了equals方法

 Comparable介面只提供了   int   compareTo(T   o)方法,也就是說假如我定義了一個Person類,這個類實現了   Comparable介面,那麼當我例項化Person類的person1後,我想比較person1和一個現有的Person物件person2的大小時,我就可以這樣來呼叫:person1.comparTo(person2),通過返回值就可以判斷了;而此時如果你定義了一個   PersonComparator(實現了Comparator介面)的話,那你就可以這樣:PersonComparator   comparator=   new   PersonComparator();
comparator.compare(person1,person2);。

兩種方法各有優劣, 用Comparable 簡單, 只要實現Comparable 介面的物件直接就成為一個可以比較的物件,但是需要修改原始碼, 用Comparator 的好處是不需要修改原始碼, 而是另外實現一個比較器, 當某個自定義的物件需要作比較的時候,把比較器和物件一起傳遞過去就可以比大小了, 並且在Comparator 裡面使用者可以自己實現複雜的可以通用的邏輯,使其可以匹配一些比較簡單的物件,那樣就可以節省很多重複勞動了。

Comparable

Comparable 定義在 Person類的內部:

public class Persion implements Comparable {..比較Person的大小..},

 因為已經實現了比較器,那麼我們的Person現在是一個可以比較大小的物件了,它的比較功能和String完全一樣,可以隨時隨地的拿來
比較大小,因為Person現在自身就是有大小之分的。Collections.sort(personList)可以得到正確的結果。

Comparator

Comparator 是定義在Person的外部的, 此時我們的Person類的結構不需要有任何變化,如

public class Person{ String name; int age },

然後我們另外定義一個比較器:

public PersonComparator implements Comparator() {..比較Person的大小..},

在PersonComparator裡面實現了怎麼比較兩個Person的大小. 所以,用這種方法,當我們要對一個 personList進行排序的時候, 
我們除了了要傳遞personList過去, 還需要把PersonComparator傳遞過去,因為怎麼比較Person的大小是在PersonComparator
裡面實現的, 如:

Collections.sort( personList , new PersonComparator() ).

Arrays.sort(T[], Comparator < ? super T > c) 方法用於物件陣列按使用者自定義規則排序.

使用策略模式
這是策略模式(Strategy pattern)的一個完美又簡潔的示例,值得一提的是為什麼這種場景下適合使用策略模式.
總體來說,策略模式允許在程式執行時選擇不同的演算法.比如在排序時,傳入不同的比較器(Comparator),就採用不同的演算法.
根據上面的例子,假設你想要根據Dog的重量來進行排序,可以像下面這樣,建立一個新的比較器來進行排序:

  1. class Dog{  
  2.     int size;  
  3.     int weight;  
  4.     public Dog(int s, int w){  
  5.         size = s;  
  6.         weight = w;   
  7.     }  
  8. }  
  9. class DogSizeComparator implements Comparator<Dog>{  
  10.     @Override
  11.     publicint compare(Dog o1, Dog o2) {  
  12.         return o1.size - o2.size;  
  13.     }  
  14. }  
  15. class DogWeightComparator implements Comparator<Dog>{  
  16.     @Override
  17.     publicint compare(Dog o1, Dog o2) {  
  18.         return o1.weight - o2.weight;  
  19.     }  
  20. }  
  21. publicclass ArraySort {  
  22.     publicstaticvoid main(String[] args) {  
  23.         Dog d1 = new Dog(250);  
  24.         Dog d2 = new Dog(130);  
  25.         Dog d3 = new Dog(340);  
  26.         Dog[] dogArray = {d1, d2, d3};  
  27.         printDogs(dogArray);  
  28.         Arrays.sort(dogArray, new DogSizeComparator());   
  29.         printDogs(dogArray);  
  30.         Arrays.sort(dogArray, new DogWeightComparator());     
  31.         printDogs(dogArray);  
  32.     }  
  33.     publicstaticvoid printDogs(Dog[] dogs){  
  34.         for(Dog d: dogs)  
  35.             System.out.print("size="+d.size + " weight=" + d.weight + " ");  
  36.         System.out.println();  
  37.     }  
  38. }  
執行結果:
  1. size=2 weight=50 size=1 weight=30 size=3 weight=40  
  2. size=1 weight=30 size=2 weight=50 size=3 weight=40  
  3. size=1 weight=30 size=3 weight=40 size=2 weight=50  
Comparator 是一個介面,所以sort方法中可以傳入任意實現了此介面的類的例項,這就是策略模式的主要思想.

3. 為何使用"super"
如果使用 "Comparator < T > c" 那是很簡單易懂的,但是sort的第2個引數裡面的 < ? super T > 意味著比較器所接受的型別可以是T或者它的超類. 為什麼是超類呢? 答案是: 這允許使用同一個比較器對不同的子類物件進行比較.在下面的示例中很明顯地演示了這一點:
  1. import java.util.Arrays;  
  2. import java.util.Comparator;  
  3. class Animal{  
  4.     int size;  
  5. }  
  6. class Dog extends Animal{  
  7.     public Dog(int s){  
  8.         size = s;  
  9.     }  
  10. }  
  11. class Cat extends Animal{  
  12.     public Cat(int s){  
  13.         size  = s;  
  14.     }  
  15. }  
  16. class AnimalSizeComparator implements Comparator<Animal>{  
  17.     @Override
  18.     publicint compare(Animal o1, Animal o2) {  
  19.         return o1.size - o2.size;  
  20.     }  
  21.     //in this way, all sub classes of Animal can use this comparator.
  22. }  
  23. publicclass ArraySort {  
  24.     publicstaticvoid main(String[] args) {  
  25.         Dog d1 = new Dog(2);  
  26.         Dog d2 = new Dog(1);  
  27.         Dog d3 = new Dog(3);  
  28.         Dog[] dogArray = {d1, d2, d3};  
  29.         printDogs(dogArray);  
  30.         Arrays.sort(dogArray, new AnimalSizeComparator());    
  31.         printDogs(dogArray);  
  32.         System.out.println();  
  33.         //when you have an array of Cat, same Comparator can be used. 
  34.         Cat c1 = new Cat(2);  
  35.         Cat c2 = new Cat(1);  
  36.         Cat c3 = new Cat(3);  
  37.         Cat[] catArray = {c1, c2, c3};  
  38.         printDogs(catArray);  
  39.         Arrays.sort(catArray, new AnimalSizeComparator());    
  40.         printDogs(catArray);  
  41.     }  
  42.     publicstaticvoid printDogs(Animal[] animals){  
  43.         for(Animal a: animals)  
  44.             System.out.print("size="+a.size + " ");  
  45.         System.out.println();  
  46.     }  
  47. }  
輸出結果:
  1. size=2 size=1 size=3  
  2. size=1 size=2 size=3  
  3. size=2 size=1 size=3  
  4. size=1 size=2 size=3  

4. 小結
與Arrays.sort()相關的資訊總結如下:
  1. 通用: super 類
  2. 策略設計模式(strategy pattern);
  3. 歸併排序(merge sort): 時間複雜度 n*log(n);
  4. Java.util.Collections#sort(List < T > list, Comparator < ? super T > c)與Arrays.sort 使用類似的思想.