大家對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/54317539JDK8新特性:使用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; } @Override public String toString() { return "Student [id=" + id + ", age=" + age + ", name=" + name + ", address=" + address + "]"; } @Override 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; } @Override 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; } @Override 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;}
新聞熱點
疑難解答