大家對java接口Comparator和Comparable都不陌生,JDK8里面Comparable還和以前一樣,沒有什么改動;但是Comparator在之前基礎上增加了很多static和default方法。本文主要結合JDK的stream編程,學習下Comparator。閱讀本文需要一些前置知識,可以參考如下文章。
JDK8新特性:接口的靜態方法和默認方法
http://blog.csdn.net/aitangyong/article/details/54134385
JDK8新特性:函數式接口@FunctionalInterface的使用說明
http://blog.csdn.net/aitangyong/article/details/54137067
JDK8新特性:lambda入門
http://blog.csdn.net/aitangyong/article/details/54317539
JDK8新特性:使用Method References實現方法復用,簡化lambda表達式
http://blog.csdn.net/aitangyong/article/details/54586197
可以使用Stream.sort對集合進行排序,sort有2個重載方法,區別如下。

-
// Student實現Comparable接口,默認按照id升序排列
-
public class Student implements Comparable<Student>{
-
-
private int id;
-
-
private int age;
-
-
private String name;
-
-
private Address address;
-
-
public Student(int id, int age, String name, Address address) {
-
this.id = id;
-
this.age = age;
-
this.name = name;
-
this.address = address;
-
}
-
-
public int getId() {
-
return id;
-
}
-
-
public void setId(int id) {
-
this.id = id;
-
}
-
-
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;
-
}
-
-
public Address getAddress() {
-
return address;
-
}
-
-
public void setAddress(Address address) {
-
this.address = address;
-
}
-
-
-
public String toString() {
-
return "Student [id=" + id + ", age=" + age + ", name=" + name + ", address=" + address + "]";
-
}
-
-
-
public int compareTo(Student o) {
-
return this.id - o.id;
-
}
-
-
}
stream().sorted()/Comparator.naturalOrder()/Comparator.reverseOrder(),要求元素必須實現Comparable接口。
-
import java.util.ArrayList;
-
import java.util.Comparator;
-
import java.util.List;
-
import java.util.stream.Collectors;
-
-
public class TestComparator {
-
-
public static void main(String[] args) {
-
List<Student> students = buildStudents();
-
-
// 按照默認順序排序
-
List<Student> ascList1 = students.stream().sorted().collect(Collectors.toList());
-
System.out.println(ascList1);
-
-
// 按照自然序排序(其實就是默認順序)
-
List<Student> ascList2 = students.stream().sorted(Comparator.naturalOrder()).collect(Collectors.toList());
-
System.out.println(ascList2);
-
-
// 按照默認順序的相反順序排序
-
List<Student> descList = students.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
-
System.out.println(descList);
-
-
}
-
-
private static List<Student> buildStudents() {
-
List<Student> students = new ArrayList<>();
-
students.add( new Student(10, 20, "aty", new Address("d")));
-
students.add( new Student(1, 22, "qun", new Address("c")));
-
students.add( new Student(1, 26, "Zen", new Address("b")));
-
students.add( new Student(5, 23, "aty", new Address("a")));
-
return students;
-
}
-
-
}
-
-

如果Student沒有實現Comparable接口,效果如下:

接下來測試,都不要求Student實現Comparable接口,這里直接給出Student和Address實體類。
-
public class Student {
-
-
private int id;
-
-
private int age;
-
-
private String name;
-
-
private Address address;
-
-
public Student(int id, int age, String name, Address address) {
-
this.id = id;
-
this.age = age;
-
this.name = name;
-
this.address = address;
-
}
-
-
public int getId() {
-
return id;
-
}
-
-
public void setId(int id) {
-
this.id = id;
-
}
-
-
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;
-
}
-
-
public Address getAddress() {
-
return address;
-
}
-
-
public void setAddress(Address address) {
-
this.address = address;
-
}
-
-
-
public String toString() {
-
return "Student [id=" + id + ", age=" + age + ", name=" + name + ", address=" + address + "]";
-
}
-
-
}
-
public class Address {
-
private String address;
-
-
public Address(String address) {
-
super();
-
this.address = address;
-
}
-
-
public String getAddress() {
-
return address;
-
}
-
-
public void setAddress(String address) {
-
this.address = address;
-
}
-
-
-
public String toString() {
-
return "Address [address=" + address + "]";
-
}
-
-
-
}
Comparator.comparing(Function keyExtractor)生成1個Comparator對象,要求keyExtractor.apply()返回值一定要實現Comparable接口。比如下面代碼extractIdWay1和extractIdWay2都是等價的,從Student對象中提取id屬性,而id是int類型(Integer實現了Comparable)。
-
import java.util.ArrayList;
-
import java.util.Comparator;
-
import java.util.List;
-
import java.util.function.Function;
-
import java.util.stream.Collectors;
-
-
public class TestComparator {
-
-
public static void main(String[] args) {
-
List<Student> students = buildStudents();
-
-
// 使用lambda表達式創建Function對象
-
Function<Student, Integer> extractIdWay1 = (student) -> student.getId();
-
-
// 使用方法引用簡化lambda
-
Function<Student, Integer> extractIdWay2 = Student::getId;
-
-
// Comparator.comparing(Function keyExtractor)
-
Comparator<Student> byId = Comparator.comparing(extractIdWay2);
-
-
// 升序
-
List<Student> ascList = students.stream().sorted(byId).collect(Collectors.toList());
-
System.out.println(ascList);
-
-
// 降序
-
List<Student> descList = students.stream().sorted(byId.reversed()).collect(Collectors.toList());
-
System.out.println(descList);
-
-
}
-
-
private static List<Student> buildStudents() {
-
List<Student> students = new ArrayList<>();
-
students.add( new Student(10, 20, "aty", new Address("d")));
-
students.add( new Student(1, 22, "qun", new Address("c")));
-
students.add( new Student(1, 26, "Zen", new Address("b")));
-
students.add( new Student(5, 23, "aty", new Address("a")));
-
return students;
-
}
-
-
}
由於Student.getAddress()返回的對象沒有實現Comparable接口,所以不能通過Comparator.comparing()創建一個Comparator對象。
如果我們想安裝Address(沒有實現Comparable接口)排序怎么辦呢?使用另一種形式的comparing方法:

-
import java.util.ArrayList;
-
import java.util.Comparator;
-
import java.util.List;
-
import java.util.stream.Collectors;
-
-
public class TestComparator {
-
-
public static void main(String[] args) {
-
List<Student> students = buildStudents();
-
-
Comparator<Address> cmpAddr = Comparator.comparing(Address::getAddress);
-
Comparator<Student> byAddress = Comparator.comparing(Student::getAddress, cmpAddr);
-
List<Student> sortedAddressList = students.stream().sorted(byAddress).collect(Collectors.toList());
-
System.out.println(sortedAddressList);
-
}
-
-
private static List<Student> buildStudents() {
-
List<Student> students = new ArrayList<>();
-
students.add( new Student(10, 20, "aty", new Address("d")));
-
students.add( new Student(1, 22, "qun", new Address("c")));
-
students.add( new Student(1, 26, "Zen", new Address("b")));
-
students.add( new Student(5, 23, "aty", new Address("a")));
-
return students;
-
}
-
-
}
這種形式的comparing()接收2個參數,第一個參數提取要排序的key,第二個參數指定排序的Comparator。自己指定比較器,可以靈活定制比較邏輯。比如,我們想實現字符串不區分大小寫比較。
-
//getName()返回String本身已經實現了Comparable,但是我們可以自己傳遞一個不區分大小寫的比較器
-
Comparator<Student> byName = Comparator.comparing(Student::getName, String.CASE_INSENSITIVE_ORDER);
-
List<Student> sortedNameList = students.stream().sorted(byName).collect(Collectors.toList());
-
System.out.println(sortedNameList);
comparingDouble()、comparingLong()、comparingInt()不過是comparing()更具體的版本,使用方式相同。
-
public static void main(String[] args) {
-
List<Student> students = buildStudents();
-
-
Comparator<Student> byAge1 = Comparator.comparingInt(Student::getAge);
-
Comparator<Student> byAge2 = Comparator.comparing(Student::getAge);
-
List<Student> sortedAgeList1 = students.stream().sorted(byAge1).collect(Collectors.toList());
-
List<Student> sortedAgeList2 = students.stream().sorted(byAge2).collect(Collectors.toList());
-
System.out.println(sortedAgeList1);
-
System.out.println(sortedAgeList2);
-
}
-
-
private static List<Student> buildStudents() {
-
List<Student> students = new ArrayList<>();
-
students.add( new Student(10, 20, "aty", new Address("d")));
-
students.add( new Student(1, 22, "qun", new Address("c")));
-
students.add( new Student(1, 26, "Zen", new Address("b")));
-
students.add( new Student(5, 23, "aty", new Address("a")));
-
return students;
-
}
Comparator.nullsFirst()和Comparator.nullsLast(),前面我們創建的Student列表中沒有null,如果有null的話,上面的代碼都會拋異常。而這2個方法就是用來處理null的,一個認為null比所有非null都小,一個認為比所有都大。
-
public class TestComparator {
-
-
public static void main(String[] args) {
-
List<Student> students = buildStudents();
-
Comparator<Student> nullNotAllowed = Comparator.comparing(Student::getId);
-
Comparator<Student> allowNullComparator = Comparator.nullsFirst(nullNotAllowed);
-
-
// 正常排序
-
List<Student> result1 = students.stream().sorted(allowNullComparator).collect(Collectors.toList());
-
System.out.println(result1);
-
-
// 拋異常
-
List<Student> result2 = students.stream().sorted(nullNotAllowed).collect(Collectors.toList());
-
System.out.println(result2);
-
-
}
-
-
private static List<Student> buildStudents() {
-
List<Student> students = new ArrayList<>();
-
students.add( new Student(10, 20, "aty", new Address("d")));
-
students.add( new Student(1, 22, "qun", new Address("c")));
-
students.add( new Student(1, 26, "Zen", new Address("b")));
-
students.add( new Student(5, 23, "aty", new Address("a")));
-
students.add( null);
-
return students;
-
}
-
-
}

至此Comparator的static方法已經介紹完畢,接下來我們看下它的default方法。
reversed()前面已經介紹了,返回一個新的比較器(排序順序相反)
thenComparing()系列方法與comparing()使用方法類似

如果我們先按照id排序,id相等的話再按照name排序,那么可以這樣寫。
-
public static void main(String[] args) {
-
List<Student> students = buildStudents();
-
-
// id升序
-
Comparator<Student> byIdASC = Comparator.comparing(Student::getId);
-
-
// named不分區大小寫降序
-
Comparator<Student> byNameDESC = Comparator.comparing(Student::getName, String.CASE_INSENSITIVE_ORDER)
-
.reversed();
-
-
// 聯合排序
-
Comparator<Student> finalComparator = byIdASC.thenComparing(byNameDESC);
-
-
List<Student> result = students.stream().sorted(finalComparator).collect(Collectors.toList());
-
System.out.println(result);
-
}
-
-
private static List<Student> buildStudents() {
-
List<Student> students = new ArrayList<>();
-
students.add( new Student(10, 20, "aty", new Address("d")));
-
students.add( new Student(1, 22, "qun", new Address("c")));
-
students.add( new Student(1, 26, "Zen", new Address("b")));
-
students.add( new Student(5, 23, "aty", new Address("a")));
-
return students;
-
}

