1. 前言
排序算是比較高頻的面試題了,節前面試了的兩家公司都有問到排序問題,整理後分享給大家(文末見總結)。
通常我們想到實作排序就是 Collections 工具類的 sort() 方法,而 sort() 方法有兩種:
- 直接調用 Collections.sort(List list) 方法進行排序(正序排序)。
- 調用 Collections.sort(List list,Comparator<? super T> c) ,自定義實作 Comparator 接口,重新 compareTo 方法。(相當于指定排序規則)
下面來詳細說明這兩個方法。
2. 什麼是 Comparator ?
在看方法之前,我們先來看看這個 Comparator 到底有什麼用?
Comparator ,中文意思為「比較器」,比較器的出現就是有些自定義類的 List 集合,不支援自身比較或者自身比較函數不能滿足需求時,然後就可以寫一個比較器來完成兩個對象大小之間的比較,上邊提到的方法 2 就是指定使用自定義 Comparator 來實作比較;而方法 1 是不指定 Comparator,不指定就會使用預設的排序方式(正序)。
3. List 屬性的不同
List 排序一般分為「單屬性排序」以及「實體屬性排序」,單屬性就是像是 String、Integer 等封裝類(List list,List list),這些基本類型的封裝類都已經實作了 Comparable 接口,并重寫 compareTo() 方法,實體屬性則就是我們自定義的實體類,然後想通過某些屬性對象進行排序。
單屬性
我們拿 Integer 類為例:
List<Integer> list = new ArrayList<Integer>();
如下為 Integer 内部的實作接口截圖:

是以,當我們直接調用 Collections.sort() 方法就可以實作排序效果(正序),舉例說明:
這就是單屬性集合的排序,直接調用 Collections.sort() 接口就能完成排序。
4. Comparable 和 Comparator 差別
然後就可能有小夥伴說,哎?你不對勁...
你上邊說的是 Comparator 比較器,現在怎麼 Integer 實作的是 Comparable 接口?Comparable 又是個什麼鬼,這倆單詞怎麼長的這麼像... 跟 Comparator 到底有啥差別呀?
咳咳,不要慌...
其實我們可以先看一下 Comparable 接口,該接口内部就隻有一個 compareTo() 方法:
再來看看 Comparator 接口,Comparator 接口的内部方法就比較多了,當然,在這我們就關注一下 compare() 方法即可:
我們再回到 Integer 類,Integer 實作了 Comparable 接口,是以我們找一下 compareTo() 方法如下:
如上方法,compareTo() 方法内部調用了 Integer 内部的 compare() 方法,通過注釋我們發現。
Integer 内部完成了數值的比較?
其實到這也有點眉目了,好多文章有這麼一個說法:Comparable 屬于内比較器,Comparator 屬于外比較器
所謂的内比較器,我們還是以 Integer 為例,Integer 實作了 Comparable 接口,從 Integer 内部完成了數值的比較,也就是拿另外一個值跟自身比。
所謂的外比較器,就是他會拿一個單獨的類來完成比較,這個時候我們就可以拿方法二來看了:
通過上面 List 的例子我們了解到了 Comparator 跟 Comparable 的使用,使用這兩種方式都可以完成單屬性的排序,差別就是内外部實作不同。
排序規則:
o1大于o2,傳回正整數
o1等于o2,傳回0
o1小于o3,傳回負整數
5. 實體屬性排序
因為平時工作中還是以實體屬性 List 排序為主,并不會是直接 List,是以下面我們就通過一個 List 例子來分别通過 Comparator、Comparable 實作排序。
User.java
public class User {
/**使用者年齡**/
private Integer age;
public User(Integer age){
this.age = age;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
5.1 Comparator 方式
代碼及執行截圖:
5.2 Comparable 方式
我們需要讓 User.java 實體實作一個 Comparable 接口,并重寫 compareTo() 方法:
public class User implements Comparable<User>{
private Integer age;
public User(Integer age){
this.age = age;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public int compareTo(User o) {
// return this.age - o.getAge(); 正序
return o.getAge() - this.age; // 倒序
}
}
然後我們測試一下:
ok,到這我們就實作了兩種方式完成對實體屬性對象的集合進行排序。
6. 總結
實作排序有兩種方式:
- 對象内部實作 Comparable 接口,重新 compareTo() 方法(區分正序倒序),完成内部比較,然後調用 Collections.sort() 排序。
- 建立一個實作了 Comparator 接口的類,并重寫 compare() 抽象方法
如下轉自:https://my.oschina.net/sdlvzg/blog/2243766
JDK1.8之後可以很友善的對 List 進行排序,不用再寫 Collections 類了。
6.1 基礎類型 List 排序
/* 對數字進行排序 */
List<Integer> nums = Arrays.asList(3,1,5,2,9,8,4,10,6,7);
nums.sort(Comparator.reverseOrder()); /* reverseOrder倒序 */
System.err.println("倒序:"+nums);
nums.sort(Comparator.naturalOrder()); /* naturalOrder自然排序即:正序 */
System.err.println("正序:"+nums);
執行結果如下:
6.2 對象List單屬性排序
List<User> listDevs = new ArrayList<User>(){{
add(new User(10));
add(new User(9));
add(new User(20));
add(new User(4));
}};
System.out.println("排序前:");
/*JAVA8的寫法,循環*/
listDevs.forEach((developer)->System.out.println(developer.getAge()));
/*第一個寫法*/
Collections.sort(listDevs, new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getAge().compareTo(o2.getAge());
}
});
/*第二個寫法,JAVA8的寫法,List 接口支援直接使用 sort 該方法,不再需要使用 Collections.sort 了
listDevs.sort(listDevs, new Comparator<Developer>() {
@Override
public int compare(Developer o1, Developer o2) {
return o1.getAge().compareTo(o2.getAge();
}
});*/
/*第三個寫法,Lambda寫法,JAVA8的寫法
listDevs.sort((Developer o1, Developer o2)->o1.getAge().compareTo(o2.getAge()));*/
/*第四個寫法,Lambda寫法,JAVA8的寫法
listDevs.sort((o1, o2)->o1.getAge().compareTo(o2.getAge()));*/
/*第五寫法,個Lambda寫法,JAVA8的寫法
listDevs.sort(Comparator.comparing(Developer::getAge));*/
/*第六寫法,個Lambda寫法,JAVA8的寫法*/
Comparator<User> ageComparator = (o1, o2)->o1.getAge().compareTo(o2.getAge());
listDevs.sort(ageComparator); /*按上面配置的順序取值*/
listDevs.sort(ageComparator.reversed()); /*按上面配置的順序反向取值*/
System.out.println("排序後:");
/*JAVA8的寫法,循環*/
listDevs.forEach((developer)->System.out.println(developer.getAge()));
部落格園持續更新:https://www.cnblogs.com/niceyoo
執行截圖: