Arrays.sort排序方法以及Comparator和Comparable接口的作用

有的時候需要對數組裏的element進行排序。當然可以自己編寫合適的排序方法,但既然Java包裏有自帶的Arrays.sort排序方法,在數組元素比較少的時候爲何不用?

  Sorting an Array 1. 數字排序  int[] intArray = new int[] { 4, 1, 3, -23 };

  Arrays.sort(intArray);

  輸出: [-23, 1, 3, 4]

  2. 字符串排序,先大寫後小寫 String[] strArray = new String[] { "z", "a", "C" };

  Arrays.sort(strArray);

  輸出: [C, a, z]

  3. 嚴格按字母表順序排序,也就是忽略大小寫排序 Case-insensitive sort

  Arrays.sort(strArray, String.CASE_INSENSITIVE_ORDER);

  輸出: [a, C, z]

  4. 反向排序, Reverse-order sort

  Arrays.sort(strArray, Collections.reverseOrder());

  輸出:[z, a, C]

  5. 忽略大小寫反向排序 Case-insensitive reverse-order sort

  Arrays.sort(strArray, String.CASE_INSENSITIVE_ORDER);

  Collections.reverse(Arrays.asList(strArray));

  輸出: [z, C, a]

  java初學者最常見的錯誤思想,就是試圖去寫一些方法來完成數組的排序功能,其實,數組排序功能,在java的api裏面早已實現,我們沒有必要去重複製造輪子。

  Arrays類有一個靜態方法sort,利用這個方法我們可以傳入我們要排序的數組進去排序,因爲我們傳入的是一個數組的引用,所以排序完成的結果也通過這個引用的來更改數組.對於整數、字符串排序,jdk提供了默認的實現,如果要對一個對象數組排序,則要自己實現java.util.Comparator接口。

  package demo1.client;

  import java.util.Arrays;

  import java.util.Comparator;

  public class ArraySortDemo {

  public void sortIntArray() {

  int[] arrayToSort = new int[] { 48, 5, 89, 80, 81, 23, 45, 16, 2 };

  System.out.println("排序前");

  for (int i = 0; i < arrayToSort.length; i++)

  System.out.println(arrayToSort[i]);

  // 調用數組的靜態排序方法sort

  Arrays.sort(arrayToSort);

  System.out.println("排序後");

  for (int i = 0; i < arrayToSort.length; i++)

  System.out.println(arrayToSort[i]);

  }

  public void sortStringArray() {

  String[] arrayToSort = new String[] { "Oscar", "Charlie", "Ryan",

  "Adam", "David" };

  System.out.println("排序前");

  for (int i = 0; i < arrayToSort.length; i++)

  System.out.println(arrayToSort[i]);

  System.out.println("排序後");

  //調用數組的靜態排序方法sort

  Arrays.sort(arrayToSort);

  for (int i = 0; i < arrayToSort.length; i++)

  System.out.println(arrayToSort[i]);

  }

  public void sortObjectArray() {

  Dog o1 = new Dog("dog1", 1);

  Dog o2 = new Dog("dog2", 4);

  Dog o3 = new Dog("dog3", 5);

  Dog o4 = new Dog("dog4", 2);

  Dog o5 = new Dog("dog5", 3);

  Dog[] dogs = new Dog[] { o1, o2, o3, o4, o5 };

  System.out.println("排序前");

  for (int i = 0; i < dogs.length; i++) {

  Dog dog = dogs[i];

  System.out.println(dog.getName());

  }

  Arrays.sort(dogs, new ByWeightComparator());

  System.out.println("排序後:");

  for (int i = 0; i < dogs.length; i++) {

  Dog dog = dogs[i];

  System.out.println(dog.getName());

  }

  }

  public static void main(String[] args) {

  ArraySortDemo t = new ArraySortDemo();

  t.sortIntArray();

  t.sortStringArray();

  t.sortObjectArray();

  }

  }

  class Dog {

  private String name;

  private int weight;

  public Dog(String name, int weight) {

  this.setName(name);

  this.weight = weight;

  }

  public int getWeight() {

  return weight;

  }

  public void setWeight(int weight) {

  this.weight = weight;

  }

  public void setName(String name) {

  this.name = name;

  }

  public String getName() {

  return name;

  }

  }

  class ByWeightComparator implements Comparator {

  public final int compare(Object pFirst, Object pSecond) {

  int aFirstWeight = ((Dog) pFirst).getWeight();

  int aSecondWeight = ((Dog) pSecond).getWeight();

  int diff = aFirstWeight - aSecondWeight;

  if (diff > 0)

  return 1;

  if (diff < 0)

  return -1;

  else

  return 0;

  }

  }


在我的印象裏,對於數組排序,最簡單的方法就是使用Arrays.sort(數組a);它也使我理所應當的認爲:Arrays這個類完全實現這個sort的方法,而實際上並非如此,先看下sort的原型:public static <T extends Comparable<? super T>> void sort(List<T> list),藍色部分即是說你要排序的這個集合元素已經實現了接口Comparable,等等,我好像沒有實現任何Comparable接口,事實上,String類和所有的包裝類對象在Java API中已經實現了這個接口。參考API:

      public final class String extends implements , <>,

      如果你嘗試爲沒有實現Comparable接口的元素排序,編譯器會出錯:The generic method sort(List<T>) of type Collections is not applicable for the arguments(ArrayList<你剛編寫的類>). The inferred type 你剛編寫的類is not a valid substitute for the bounded parameter <T extends Comparable<? super T>>。

        此接口強行對實現它的每個類的對象進行整體排序。此排序被稱爲該類的自然排序,類的 compareTo 方法被稱爲它的自然比較方法。實現此接口的對象列表(和數組)可以通過Collections.sort(和 Arrays.sort)進行自動排序。實現此接口的對象可以用作有序映射表中的鍵或有序集合中的元素,無需指定比較器。對於類C的每一個 e1 和 e2 來說,當且僅當 (e1.compareTo((Object)e2) == 0) 與 e1.equals((Object)e2) 具有相同的布爾值時,類 C 的自然排序才叫做與 equals 一致。注意,null 不是任何類的實例,即使 e.equals(null) 返回 false,e.compareTo(null) 也會拋出 NullPointerException。強烈推薦(雖然不是必需的)使自然排序與 equals 一致。這是因爲在使用其自然排序與 equals 不一致的元素(或鍵)時,沒有顯式比較器的有序集合(和有序映射表)行爲表現“怪異”。尤其是,這樣的有序集合(或有序映射表)違背了根據 equals 方法定義的集合(或映射表)的常規協定。例如,如果將兩個鍵 a 和 b 添加到一個沒有使用顯式比較器的有序集合中,使得 (!a.equals((Object)b) && a.compareTo((Object)b) == 0),則第二個 add 操作返回 false(有序集合的大小沒有增加),因爲從有序集合的角度來看,a 和 b 是等效的。   實現了comparable接口的類,其對象可以作爲SortedMap的key,或者SortedSet的元素。

       我們只能在類中實現compareTo()一次,不可能說經常來修改類的代碼實現自己想要的排序,因此如果要以不同於compareTo()方法中指定的順序排序我們的類對象,那麼該怎麼辦呢?大家一定注意到Collections.sort()有一個重載版本。 public static <T> void sort(List<T> list, Comparator<? super T> c)根據指定比較器產生的順序對指定列表進行排序。此列表內的所有元素都必須可使用指定比較器相互比較(也就是說,對於列表中的任意 e1 和 e2 元素,c.compare(e1, e2) 不得拋出 ClassCastException)。此排序被保證是穩定的:不會因調用 sort 而對相等的元素進行重新排序。 Comparator<? super T> c 第二個參數是實現了接口Comparator的實例對象。注意這兩者的區別,Comparable 接口的方法:public int compareTo(T o),絕不會是Object; 只有一個參數,而Comparator接口卻有兩個參數:int compare(T o1, T o2);

     Comparator 可以通過 Collections類的reverseOrder()這個方法強勢返回對Comparable排序的倒轉,如:Arrays.sort(a, Collections.reverseOrder()); 如果是String,就是按照逆字典順序進行排序。

      這兩個接口和集合類本身無關,但通常和集合內的元素有關,因爲集合的排序要用到它們中的方法。一個類的實例要想實現排序,必須實現Comparable,或者提供相應的Comparator。說清楚這兩者的來歷與大概的作用,以及共同點,那麼再說下他們兩者的區別。只是Comparable(可比較的)是在集合內部定義的方法實現的排序,比如,兩個人要比較身高,分辨高矮是人類固有的能力,兩個人只要站到一起就能分出誰高誰矮。Comparator(比較器)是在集合外部實現的排序。所以,如想實現排序,就需要在集合外定義Comparator接口的方法compare()或在集合內實現Comparable接口的方法compareTo(),Comparable是一個對象本身就已經支持自比較所需要實現的接口(如String Integer自己就可以完成比較大小操作)而Comparator是一個專用的比較器,當這個對象不支持自比較或者自比較函數不能滿足你的要求時,你可以寫一個比較器來完成兩個對象之間大小的比較。還有個地方就是,實現Comparable只能定義一種比較方法,但是有時候會對一個集合進行不同的排序方法,此時就可以提供別各種各樣的Comparator來對集合排序,而對於要排序的元素不需要更改,所以我覺得Comparator提供了更多的靈活性。使用這種策略來比較時,如何進行比較和兩個對象本身無關,而是由第三者(即比較器)來完成的。只要實現Comparator接口,任何一個對象都可能成爲一個“比較器”,但比較器並不是比較自己的實例,而是比較另外兩個對象,比較器在這裏充當“仲裁者”的角色,這也就是爲什麼compare()方法需要兩個參數。比如,兩個人要比較誰智商更高,靠他們自身無法進行,這時要藉助一個比較器(比如,智商測試題)。那麼先看一個非常簡單易懂的示例:

import java.util.*;
public class Pockets {
   public static void main(String[] args) {
      String[] sa = {"nickel", "button", "key", "lint"};
      Sorter s = new Sorter();    //新建一個排序器
      for(String s2: sa){ System.out.print(s2 + " "); }   
      Arrays.sort(sa, s);    //這個排序器s是與sa這個類對象是相關。
      System.out.println();
      for(String s2:sa){ System.out.print(s2+" "); }
     }
   static class Sorter implements Comparator<String>{  //注意紅色的部分就是你要去排序的類,如果你文不對題,硬讓這個類去排序一個未知的class Dog的類,會報錯:The method sort(T[], Comparator<? super T>) in the type Arrays is not applicable for the arguments (UU[], Pockets.Sorter)
      public int compare(String a, String b){
         return a.compareTo(b);
      }
   }
}

        再在更加複雜的概念上理解這個排序機制,在網上找到一個例子,維護一個簡單的員工數據庫,每個員工是一個Employee類的實例。Employee類可定義爲:

public class Employee {    
    private String num;
    private String name;
    private int age;
    private int salary;
    
    public Employee(String num, String name) {
        this.num = num;
        this.name = name;
    }
    
    public void setName(String newNum) {
        num = newNum;
    }

    public void setAge(int newAge) {
        age = newAge;
    }

    public void setSalary(int newSalary) {
        salary = newSalary;
    }

    public String getNum() {
        return num;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public int getSalary() {
        return salary;
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("Employee Information:");
        sb.append("\n");
        sb.append("Number:");
        sb.append(getNum());
        sb.append("\n");
        sb.append("Name:");
        sb.append(getName());
        sb.append("\n");
        sb.append("Age:");
        sb.append(getAge());
        sb.append("\n");
        sb.append("Salary:");
        sb.append(getSalary());
        sb.append("\n");
        return sb.toString();
    }
}

EmployeeDatabase類創建Employee類的實例,並把它們存入集合:
Java code

import java.util.*;

public class EmployeeDatabase {    
    public static void main(String[] args) {        
        List<Employee> allEmployees = new ArrayList<Employee>();
        
        Employee employee1 = new Employee("AAA", "Barack Omaba");
        employee1.setAge(50);
        employee1.setSalary(9999);
        allEmployees.add(employee1);
        
        Employee employee2 = new Employee("BBB", "George Bush");
        employee2.setAge(60);
        employee2.setSalary(5999);
        allEmployees.add(employee2);
        
        System.out.println(allEmployees);

    }

}

       現在,你需要檢索所有員工,並讓他們按一定順序顯示(比如按年齡遞增),這時需要用到Collections.sort()方法。Collections.sort()有兩種策略:一種是讓集合元素本身實現Comparable接口,另一種是使用用戶提供的比較器(即Comparator)。使用第一種策略時,必須修改元素類的定義,讓它實現Comparable,因此,你必須把Employee類修改爲:

public class Employee implements Comparable<Employee> {    
    public int compareTo(Employee another) {
        return getAge() - another.getAge();
    }    
    // 其餘部分不變
}

      說明一下,因爲compareTo()方法約定:本對象大於另一個對象時,返回大於0的整數,小於時返回小於0的整數,等於時返回0。所以,可以直接返回兩者年齡的差,來實現按年齡比較。這樣就可以在main()方法中使用Collections.sort(allEmployees);來對員工按年齡排序了。但是,這種排序是非常不靈活的:第一,需要修改集合元素類Employee,而很多情況下,我們沒有辦法修改公共的類。第二,沒有辦法實現多種方式排序,如按編號,按姓名,按薪水等等。這時需要使用另一種策略,即Comparator。Comparator使用其compare()方法返回的整數來比較兩個對象,規則和compareTo()一樣。如同樣實現年齡比較,使用Comparator時,無需修改Employee類,可以在排序的時候定義相應的比較器。使用Collections.sort()方法的另一個版本:
Collections.sort(allEmployees, new Comparator<Employee>() {
    public int compare(Employee one, Employee another) {
        return one.getAge() - another.getAge();
    }
});

這裏使用了匿名內部類,實際上相當於先定義一個比較器類,如:

class EmployeeComparator implements Comparator<Employee> {    
    public int compare(Employee one, Employee another) {
        return one.getAge() - another.getAge();
    }
    
}

再使用:
Collections.sort(allEmployees, new EmployeeComparator());
可以看到,比較器完全獨立於元素類Employee,因此可以非常方便地修改排序規則。你還可以定義一系列比較器,供排序時選擇使用,如:

// 按薪水升序
class EmployeeSalaryAscendingComparator implements Comparator<Employee> {    
    public int compare(Employee one, Employee another) {
        return one.getSalary() - another.getSalary();
    }
    
}

// 按薪水降序
class EmployeeSalaryDescendingComparator implements Comparator<Employee> {
    
    public int compare(Employee one, Employee another) {
        return another.getSalary() - one.getSalary();
    }
    
}

相應的使用方法如:
Collections.sort(allEmployees, new EmployeeSalaryAscendingComparator());
Collections.sort(allEmployees, new EmployeeSalaryDescendingComparator());

       使用Comparator時,元素類無需實現Comparable,因此我們保持最初版本的Employee,但實際應用中,可以用Comparable的compareTo()方法來定義默認排序方式,用Comparator定義其他排序方式。實現其compare(T o1,T o2)有一個約定,就是 a.compare(b) == 0 和a.equals(b)要有相同的boolean結果。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章