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);来对员工按年龄排序了。
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提供了更多的灵活性。