如何理解java裡的Comparator和Comparable
Comparator,又名比較器,是為了比較兩個物件的大小而抽象出的一個介面。
在排序的時候常需要實現這個介面來定製比較規則。
但是很多人用的時候不清楚該如何使用這個介面,下面我就講一下這個介面的正確使用方法!
這個接口裡有一個必須實現的方法(因為java8之後有的接口裡的部分方法是有預設實現的,所以
不再是接口裡的每個方法都必須實現了)
public int compare(Object o1, Object o2) { return 0; }
只要重寫這個方法,就能夠定製自己想要的排序規則,但是這個方法該如何正確的使用呢?
從方法名上看,這個方法叫做比較(compare),這是一個及其模糊的名字,因為比較本身
就是雙向的定義,本來就是個不清楚的定義,比如o1比o2小算是比較,而o2比o1大也是比較
可是它們表述的是同一件事情!!!
因此這個函式在使用的時候常常產生歧義,如果要使用它就得先理解,這裡的比較指的是,o1比較o2
也就是說如果o1比o2小那麼返回負值,如果o1比o2大那麼返回正值,否則返回0。然後在呼叫
排序的庫函式時(比如Arrays.sort),就可以得到一個升序的排列。
比如:
Integer[] nums = new Integer[]{1,7,3,5,4}; Arrays.sort(nums, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { if(o1 < o2)return -1; if(o1 > o2)return 1; return 0; } }); for (int i:nums) System.out.println(i);
輸出結果為
1
3
4
5
7
不過由於定義本身的雙向性 java裡並沒有提供一個降序函式。
因此如果要實現降序,那麼需要在compare函式中把邏輯反過來
但是這樣的話就很容易越搞越亂,因此一般只寫升序的邏輯。
它有一個類似的介面Comparable,這個介面往往是可比較類實現的,它不像Comparator
往往是寫成一個匿名類,換句話說如果一個類想要自身帶有可比較這個行為,那麼它就要實現這個
介面。而這個接口裡也有一個方法compareTo
public int compareTo(Object o) { return 0; }
也是比較的意思,不過這裡只有一個引數,比較是兩個物件之間的事情,因此一定有
第二個物件才能夠比較,實際上這裡有第二個物件,那就是this,也就是這個可比較類
本身,這裡相當於this是第一個引數,而o是第二個引數,只是this不需要寫出,因為這個方法就是
this自身的,從邏輯上來看相當於this比較o,理解了這個就能夠理解下面的程式碼了:
class MyEntity implements Comparable<MyEntity>{ private int val; @Override public int compareTo(MyEntity o) { if(this.val < o.val)return -1; if(this.val > o.val)return 1; return 0; } }
一般來講,Comparator使用的更多,因為如果一個類實現了Comparable介面,那麼一般來講它就具有了
一種不可變的比較方法,即使可以通過控制變數來使得compareTo這個方法具有可變性(比如使其從this比較o變成o比較this),但從面向物件的角度來看這也是不正確的,因為一個物件一旦是可比較的,那麼可比較的邏輯就應該是確定的。不過Comparator則不同,可以隨時定製,隨時根據需求隨時更改,因為它不是被可比較類實現,因此即使更改也不破環可比較類的內部比較邏輯!
最後說一下,java8之後有了lambda表示式,使得程式碼可以寫成下面這樣,使得程式碼簡化了很多:
Integer[] nums = new Integer[]{1,7,3,5,4}; Arrays.sort(nums, (o1,o2)->{ if(o1 < o2)return -1; if(o1 > o2)return 1; return 0; }); for (int i:nums) System.out.println(i);