我們通常使用Collections.sort()方法來對一個簡單的數據列表排序。但是當ArrayList是由自定義對象組成的,就需要使用comparable或者comparator接口了。在使用這兩者進行排序之前,先嘗試不實現任何接口來進行排序。
考慮下面的例子——有一個Student類,具有三個屬性:name, rollno, age
public class Student { private String name; private int rollno; private int age; /* 有參構造 ...*/ /* getter and setter ...*/ }
然后需要一個Student組成的ArrayList:
import java.util.*; public class ArrayListSorting { public static void main(String args[]){ ArrayList<Student> arraylist = new ArrayList<Student>(); arraylist.add(new Student(223, "Chaitanya", 26)); arraylist.add(new Student(245, "Rahul", 24)); arraylist.add(new Student(209, "Ajeet", 32)); Collections.sort(arraylist); for(Student str: arraylist){ System.out.println(str); } } }
這里嘗試去調用Collections.sort()方法,但是出錯了:
Exception in thread “main” java.lang.Error: Unresolved compilation problem:
Bound mismatch: The generic method sort(List) of type Collections is not applicable for the arguments (ArrayList). The inferred type Student is not a valid substitute for the bounded parameter > at beginnersbook.com.Details.main(Details.java:11)
原因:對於排序的ArrayList,除非其中元素實現了Comparable或者Comparator接口,否則不能調用Collections.sort方法。
使用Comparable對ArrayList<Object>進行排序
我們想要根據age對Student進行排序——實現Comparable接口,重寫compareTo方法。
package beginnersbook.com; public class Student implements Comparable { private String name; private int rollno; private int age; /* 有參構造 */ ... //getter and setter methods ... @Override public int compareTo(Student comparestu) { int compareAge=((Student)comparestu).getAge(); /* 正序排列 */ return this.age-compareAge; /* 逆序排列 */ //return compareAge-this.age; } @Override public String toString() { return "[ rollno=" + rollno + ", name=" + name + ", age=" + age + "]"; } }
現在我們可以正常調用Collections.sort方法了。
既然Comparable完成了我們的工作,為什么還需要Comparator呢?
使用Comparable接口我們只能根據一個屬性對ArrayList進行排序。為了讓排序可以根據多個屬性進行,需要使用Comparator。
使用Comparator對ArrayList<Object>進行排序(多個屬性)
重寫Comparator的compare方法:
import java.util.Comparator; public class Student { private String name; private int rollno; private int age; /* 有參構造 */ ... //Getter and setter methods ... /* 根據name進行排序 */ public static Comparator<Student> NameComparator = new Comparator<Student>() { public int compare(Student s1, Student s2) { String name1 = s1.getName().toUpperCase(); String name2 = s2.getName().toUpperCase(); // 正序排列 return StudentName1.compareTo(StudentName2); // 逆序排列 //return StudentName2.compareTo(StudentName1); }}; /* 根據rollno進行排序 */ public static Comparator<Student> StuRollno = new Comparator<Student>() { public int compare(Student s1, Student s2) { int rollno1 = s1.getRollno(); int rollno2 = s2.getRollno(); /* 正序排列 */ return rollno1-rollno2; /* 逆序排列 */ //return rollno2-rollno1; }}; @Override public String toString() { return "[ rollno=" + rollno + ", name=" + name + ", age=" + age + "]"; } }
這樣,我們就既可以用name進行排序,也可以用age進行排序,只需要在有不同的排序需求時,使用不同的Comparator就可以了(e.g. Collections.sort(arraylist, Student.NameComparator))。
——以上內容譯自Java ArrayList of Object Sort Example (Comparable And Comparator)
多屬性組合排序
上面對Java的ArrayList自定義排序進行了基本介紹,下面是工作中遇到的一個需求:
對於一個漏洞,有兩個排序依據,首先根據是否修復來排序,其次按照嚴重程度排序,類似於SQL中的ORDER BY field1, field2,若field1相同,則比較field2
可以看到,右邊的一列是按照未修復,已修復的順序排列的,而左邊也是按照風險程度由高到低排列,但是分別對應了是否修復的順序。這種排序可以認為也是多屬性排序,但是與上邊講到的多屬性不同,這種方式是多種屬性結合的排序方式,
public static Comparator<Loopholes> loopholesComparator = new Comparator<Loopholes>() { @Override public int compare(Loopholes l1, Loopholes l2) { Integer riskLevel1 = l1.getRiskLevel(); Integer riskLevel2 = l2.getRiskLevel(); Integer fixed1 = l1.getFixed(); Integer fixed2 = l2.getFixed(); int fixedComp = fixed1.compareTo(fixed2); if (fixedComp != 0) { return fixedComp; } else { return riskLevel1.compareTo(riskLevel2); } } };
首先用fixed1與fixed2進行比較,若兩者不相等,則直接排序,若兩者相等(即compartTo返回0),繼續比較風險等級並返回風險等級的排序結果。