前幾天在項目中遇到了一個將複雜對象進行排序的問題:計算BingMap地圖上距離當前位置5KM內發生事故(TrafficIncident)的點到當前位置的距離,並按距離升序排序。距離都算出來了,但這些TrafficIncident對象的排序卻難到了我。經同事提醒,Comparable或Comparator是一個不錯的選擇。因而在網上搜索了一些資料,總結下來。java
方式一: 實現Comparable接口ide
Comparable是java.lang包下的一個接口,該接口裏只有一個compareTo()方法:函數
package java.lang; import java.util.*; public interface Comparable<T> { public int compareTo(T o); }
Comparable翻譯爲「可比較的」,代表實現該接口的類都是能夠比較的,即實現Comparable接口的類自己就已經支持自比較,例如: String、Integer 本身就能夠完成比較大小操做,它們已經實現了Comparable接口。查看String類的源碼能夠看見是這樣聲明的(JDK1.7):this
public final class String implements java.io.Serializable, Comparable<String>, CharSequence
而Integer類的聲明以下:
編碼
public final class Integer extends Number implements Comparable<Integer>
可見它們都實現了Comparable接口,它們的實例均可以經過調用自身的compareTo()方法來比較本身,例如:
spa
String s1 = "aa"; String s2 = "ab"; System.out.println(s1.compareTo(s2)); //返回是兩字符串第一個不一樣的char的unicode編碼的差 Integer i1 = 3; Integer i2 = 2; System.out.println(i1.compareTo(i2)); //前者大返回1,後者大返回-1,相等返回0
而String類中的compareTo方法時這樣實現的:
翻譯
public int compareTo(String anotherString) { int len1 = value.length; int len2 = anotherString.value.length; int lim = Math.min(len1, len2); 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;//返回第一個不一樣字母的unicode的差 } k++; } return len1 - len2;//若是兩個字符串只有長度不一樣,則返回長度的差 }
Integer類中的compareTo()方法的實現以下:
code
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); //前者大返回1,後者大返回-1,相等返回0 }
所以,咱們能夠參照String類及Integer類對於Comparable接口的實現來實現本身的比較方式,而後調用compareTo方法便可知道他們的順序。 下面是一個例子(按Person類的年齡排序):
orm
import java.util.Arrays; public class ComparableTest { public static void main(String[] args) { Person[] persons = new Person[]{ new Person(20, "P1"), new Person(60, "P2"), new Person(50, "P3"), new Person(40, "P4") }; Arrays.sort(persons); System.out.println(); //下面代碼的結果同樣 //List<Person> personList = Arrays.asList(persons); //Collections.sort(personList); //System.out.println(personList); } } class Person implements Comparable<Person> { private int age; private String name; public Person(int age, String name) { this.age = age; this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } //實現Comparable接口的compareTo方法 @Override public int compareTo(Person o) { return this.age - o.age; } @Override public String toString() { return "Person[name=" + name + ", age=" + age + "]"; } }
執行結果是:
對象
[Person[name=P1, age=20], Person[name=P4, age=40], Person[name=P3, age=50], Person[name=P2, age=60]]
因而可知,Person對象已經經過Arrays.sort()方法按照age排序。
方式二: 實現Comparator接口
Comparator是java.util包下的一個接口,該接口裏有兩個方法compare()和equals():
package java.util; public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }
Comparator翻譯爲「比較器」,代表實現該接口的類都是一個比較器,通常在要比較的對象類型不支持自比較或者自比較函數不能知足要求時使用。使用此接口時,咱們能夠不在須要比較的類型(這裏是Person類)中實現比較過程,而是把這個過程轉移到了Comparator接口的compare方法中。因而,上面的例子須要修改成:
import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; public class ComparableTest { public static void main(String[] args) { Person[] persons = new Person[]{ new Person(20, "P1"), new Person(60, "P2"), new Person(50, "P3"), new Person(40, "P4") }; //Arrays.sort方法不支持使用Comparator比較器了,這裏只能使用Collections.sort來排序 List<Person> personList = Arrays.asList(persons); System.out.println("Before sort: \r\n" + personList); //這裏,將一個比較器(Comparator)傳遞給sort方法做爲參數,按照裏面的比較邏輯對Person進行排序 Collections.sort(personList, new PersonComparator()); System.out.println("After sort: \r\n" + personList); } } //被比較的類型不須要實現任何接口 class Person { private int age; private String name; public Person(int age, String name) { this.age = age; this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person[name=" + name + ", age=" + age + "]"; } } //這是一個比較器,用於比較Person對象 class PersonComparator implements Comparator<Person> { @Override public int compare(Person o1, Person o2) { //兩個Person對象的比較過程,固然,這裏能夠實現更多更復雜的比較過程 return o1.getAge() - o2.getAge(); //若是o1.age > o2.age,方法返回正數,爲正數正是代表哦o1 > o2 //若是o1.age = o2.age,方法返回0,返回0正是代表o1 == o1 //若是o1.age < o2.age,方法返回正數,爲負數正是代表哦o1 < o2 } }
輸出結果爲:
Before sort: [Person[name=P1, age=20], Person[name=P2, age=60], Person[name=P3, age=50], Person[name=P4, age=40]] After sort: [Person[name=P1, age=20], Person[name=P4, age=40], Person[name=P3, age=50], Person[name=P2, age=60]]
因而可知,咱們能夠將一個自定義的比較器做爲參數傳遞給Collections.sort()方法,下圖是API中的Collections.sort()方法:
Person對象已經按照比較器中的規則進行排序。若是在使用Collections.sort()方法時不提供這個Comparator(其實就是使用前文介紹的排序方式),那麼就以天然順序排序,如Collections.sort()方法的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接口中還有個equals()方法沒有實現,可程序並無報錯,緣由是實現該接口的類也是Object類的子類,而Object類已經實現了equals方法。
相同點
都是用於比較兩個對象「順序」的接口
均可以使用Collections.sort()方法來對對象集合進行排序
不一樣點
Comparable位於java.lang包下,而Comparator則位於java.util包下
Comparable 是在集合內部定義的方法實現的排序,Comparator 是在集合外部實現的排序
總結
使用Comparable接口來實現對象之間的比較時,能夠使這個類型(設爲A)實現Comparable接口,並能夠使用Collections.sort()方法來對A類型的List進行排序,以後能夠經過a1.comparaTo(a2)來比較兩個對象;
當使用Comparator接口來實現對象之間的比較時,只須要建立一個實現Comparator接口的比較器(設爲AComparator),並將其傳給Collections.sort()方法便可對A類型的List進行排序,以後也能夠經過調用比較器AComparator.compare(a1, a2)來比較兩個對象。
能夠說一個是本身完成比較,一個是外部程序實現比較的差異而已。
用 Comparator 是策略模式(strategy design pattern),就是不改變對象自身,而用一個策略對象(strategy object)來改變它的行爲。
好比:你想對整數採用絕對值大小來排序,Integer 是不符合要求的,你不須要去修改 Integer 類(實際上你也不能這麼作)去改變它的排序行爲,這時候只要(也只有)使用一個實現了 Comparator 接口的對象來實現控制它的排序就好了。
兩種方式,各有各的特色:使用Comparable方式比較時,咱們將比較的規則寫入了比較的類型中,其特色是高內聚。但若是哪天這個規則須要修改,那麼咱們必須修改這個類型的源代碼。若是使用Comparator方式比較,那麼咱們不須要修改比較的類,其特色是易維護,但須要自定義一個比較器,後續比較規則的修改,僅僅是改這個比較器中的代碼便可。