Java排序之Comparable接口和Comparator接口的比較和應用示例
一、其實很簡單,單從字面了解就基本明白
1. Comparable:“可比較的”,(欲參與比較的對象對應的元素類需實作Comparable接口)
使用這種政策來比較時,兩個對象(這裡兩個對象是指一個類的兩個不同執行個體)本身必須是“可比較的”,比較的标準由對象所在的類來定義,這種可比較的能力是對象本身固有的,是以不需要第三方參與就可以完成比較。要使得兩個對象本身是可比較的,那麼對象所在的類必須實作Comparable接口才可以。其compareTo()方法隻要一個參數,因為這裡隻有“你”“我”的關系,沒有第三方。
比如,兩個人要比較身高,分辨高矮是人類固有的能力,兩個人隻要站到一起就能分出誰高誰矮。
2. Comparator:“比較器”
使用這種政策來比較時,如何進行比較和兩個對象本身無關,而是由第三者(即比較器)來完成的。第三方比較器類需要另外專門設計:隻要實作Comparator接口,任何一個類(對象)都可能成為一個“比較器”,但比較器并不是比較自己的執行個體,而是比較其它類的兩個不同對象,比較器在這裡充當“仲裁者”的角色,這也就是為什麼compare()方法需要兩個參數。
比如,兩個人要比較誰智商更高,靠他們自身無法進行,這時要借助一個比較器(比如,智商測試題)。
注: Comparable和Comparator這兩個接口和集合接口(Collection)本身無關,但通常和集合内的元素有關,因為集合的排序要用到這兩個排序接口中的方法(二選其一)。一個類的多個執行個體要想實作排序,必須實作Comparable,或者提供相應的Comparator才能使用Collections.sort()進行排序。
這裡我們需要專門說明一下:java.util.Collections,是不屬于java的集合架構的,它是一個集合的工具類,它包含專門操作集合(Collection)的靜态方法,如排序、拷貝、比對查找等等,比如我們常用它的排序方法:Collections.sort(List), Collections.sort(List, Comparator)。
二、代碼示例
1. 假如你維護着一個簡單的員工資料庫,每個員工是一個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類的執行個體,并把它們存入集合:
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);
}
}
2. 現在,你需要檢索所有員工,并讓他們按一定順序顯示(比如按年齡遞增),這時需要用到Collections.sort()方法。
Collections.sort()有兩種政策:一種是讓集合中元素所屬類本身實作Comparable接口,另一種是使用使用者另外提供的比較器(即Comparator)。
2.1 使用第一種政策時,必須修改元素類的定義, 讓它實作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,而很多情況下,我們沒有辦法修改公共的類。
第二,沒有辦法實作多種方式排序,如按編号,按姓名,按薪水等等。
2.2 這時需要使用另一種政策,建立一個比較器類,其需要實作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定義其他排序方式。
三、總結-Camparable接口和Comparator接口的比較
1. Camparable的使用方式是:在欲比較的類内部定義實作compareTo()方法,然後再使用Collections. sort()來實作排序,而Comparator是在欲比較的類外部實作的排序。是以,如想實作排序,就需要在在類内實作Comparable接口的方法compareTo() 或類外定義實作Comparator接口的方法compare()。
Comparable是一個對象本身就已經支援自比較所需要實作的接口(如String ,Integer自己就可以完成比較大小操作),而Comparator是一個專用的比較器,當這個對象不支援自比較或者自比較函數不能滿足你的要求時,你可以寫一個比較器來完成兩個對象之間大小的比較。
2.一個類實作了Camparable接口則表明這個類的對象之間是可以互相比較的,這個類對象組成的集合就可以直接使用sort()方法排序。一般我們寫的bean都要都要實作這一接口,這也是标準javabean的規範。
3.Comparator可以看成一種算法的實作,将算法和資料分離,Comparator也可以在下面兩種場景下使用:
3.1 類的設計師沒有考慮到比較問題而沒有實作Camparable接口,我們可以通過Comparator來實作排序而不必改變類本身。比如,你使用了一個第三方的類,但這個第三方類麼有實作Camparable接口,而你又不能改它的代碼,另外,類一旦寫好後是不允許修改的,但 可以拓展,這時候隻能使用Comparator來實作排序。
3.2 可以使用多種排序标準,比如升序、降序等。
比如,實作Comparable隻能定義一種比較方法即compareTo(),但是有時候會對一個集合進行不同的排序方法,此時就可以提供别各種各樣的Comparator來對集合排序,而對于要排序的元素不需要更改,是以我覺得Comparator提供了更多的靈活性。