1. 程式人生 > >JAVA比較器排序及對比自然排序

JAVA比較器排序及對比自然排序

二、比較器排序:java.util.Comparator
  上篇部落格(自然排序)我提到了之所以提供比較器排序介面,是因為有時需要對同一物件進行多種不同方式的排序,這點自然排序 Comparable 不能實現。另外, Comparator 介面的一個好處是將比較排序演算法和具體的實體類分離了。
  翻翻 API 會發現, Arrays.sort 還有種過載形式:sort(T[] a, Comparator<? super T> c) ,這個方法引數的寫法用到了泛型,我們還沒講到。我們可以把它理解成這樣的形式: sort(Object[] a, Comparator c) ,這個方法的意思是按照比較器 c 給出的比較排序演算法,對 Object 陣列進行排序。Comparator 介面中定義了兩個方法: compare(Object o1, Object o2) 和 equals 方法,由於 equals 方法所有物件都有的方法,因此當我們實現 Comparator 介面時,我們只需重寫 compare 方法,而不需重寫 equals 方法。Comparator 介面中對重寫 equals 方法的描述是:“注意,不重寫 Object.equals(Object) 方法總是安全的。然而,在某些情況下,重寫此方法可以允許程式確定兩個不同的 Comparator 是否強行實施了相同的排序,從而提高效能。”。我們只需知道第一句話就OK了,也就是說,可以不用去想應該怎麼實現 equals 方法,因為即使我們不顯示實現 equals 方法,而是使用Object類的 equals 方法,程式碼依然是安全的。而對於第二句話,究竟是怎麼提高比較器效能的,我也不瞭解,所以就不說了。
  那麼我們來寫個程式碼,來用一用比較器排序。還是用 Student 類來做,只是沒有實現 Comparable 介面。由於比較器的實現類只用顯示實現一個方法,因此,我們可以不用專門寫一個類來實現它,當我們需要用到比較器時,可以寫個匿名內部類來實現 Comparator 。下面是我們的按姓名排序的方法:
  

public void sortByName () {  
    Student stu1 = new Student(1, "Little");  
    Student stu2 = new Student(2, "Cyntin");  
    Student stu3 = new Student(3, "Tony");  
    Student stu4 = new Student(4, "Gemini");  

    Student[] stus = new Student[4];  
    stus[0] = stu1;  
    stus[1] = stu4;  
    stus[2
] = stu3; stus[3] = stu2; System.out.println("Array: " + Arrays.toString(stus)); Arrays.sort(stus, new Comparator() { @Override public int compare(Object o1, Object o2) { if (o1 instanceof Student && o2 instanceof Student) { Student s1 = (Student) o1; Student s2 = (Student) o2; //return s1.getId() - s2.getId(); // 按Id排
return s1.getName().compareTo(s2.getName()); // 按姓名排 } return 0; } }); System.out.println("Sorted: " + Arrays.toString(stus)); }

當我們需要對Student按學號排序時,只需修改我們的排序方法中實現Comparator的內部類中的程式碼,而不用修改 Student 類。

P.S. 當然,你也可以用 Student 類實現 Comparator 介面,這樣Student就是(is a)比較器了(Comparator)。當需要使用這種排序的時候,將 Student 看作 Comparator 來使用就可以了,可以將 Student 作為引數傳入 sort 方法,因為 Student is a Comparator 。但這樣的程式碼不是個優秀的程式碼,因為我們之所以使用比較器(Comparator),其中有個重要的原因就是這樣可以把比較演算法和具體類分離,降低類之間的耦合。

當Java構造器排序和自然排序同時出現時,預設選擇比較器排序.

下面,分別給出使用兩種排序比較方式的 TreeSet 測試程式碼:


/** 
 * 使用自然排序 
 * Student必須實現Comparable介面,否則會丟擲ClassCastException 
 */  
public void testSortedSet3() {  
    Student stu1 = new Student(1, "Little");  
    Student stu2 = new Student(2, "Cyntin");  
    Student stu3 = new Student(3, "Tony");  
    Student stu4 = new Student(4, "Gemini");  

    SortedSet set = new TreeSet();  
    set.add(stu1);  
    set.add(stu3); // 若Student沒有實現Comparable介面,丟擲ClassCastException  
    set.add(stu4);  
    set.add(stu2);  
    set.add(stu4);  
    set.add(new Student(12, "Little"));  

    System.out.println(set);  
}  
/** 
 * 使用比較器排序 
 * Student可以只是個簡單的Java類,不用實現Comparable介面 
 */  
public void testSortedSet3() {  
    Student stu1 = new Student(1, "Little");  
    Student stu2 = new Student(2, "Cyntin");  
    Student stu3 = new Student(3, "Tony");  
    Student stu4 = new Student(4, "Gemini");  

    SortedSet set = new TreeSet(new Comparator() {  

        @Override  
        public int compare(Object o1, Object o2) {  
            if (o1 instanceof Student  
                    && o2 instanceof Student) {  
                Student s1 = (Student) o1;  
                Student s2 = (Student) o2;  
                return s1.getName().compareTo(s2.getName());  
            }  
            return 0;  
        }  

    });  

    set.add(stu1);  
    set.add(stu3);  
    set.add(stu4);  
    set.add(stu2);  
    set.add(stu4);  
    set.add(new Student(12, "Little"));  

    System.out.println(set);  
}  

最後,介紹個工具類,java.util.Collections。注意,這不是Collection介面。Collections很像Arrays類。Arrays提供了一系列用於對陣列操作的靜態方法,查詢排序等等。Collections也提供了一系列這樣的方法,只是它是用於處理集合的,雖然Collections類和Collection介面很像,但是不要被Collections的名字給欺騙了,它不是隻能處理Collection介面以及子介面的實現類,同樣也可以處理Map介面的實現類。