说到比较器我们第一时间会想到equals,但是equals是用来比较是否相等的,Comparator或者Comparable是用来比较顺序的(也就是排序)。
比较器的概念
确定两个对象之间的大小关系及排列顺序称为比较,能实现这个比较功能的类或方法称之为比较器,在java中只有两种比较器。
比较器的分类
内部比较器(Comparable接口)和外部比较器(Comparator接口)。
内部比较器(简单点说就是把比较器写在类的内部)的概念
类实现了Comparable接口,然后重写了compareTo方法(这个方法可以看作比较器),这个类就拥有了内部比较器。注意,你一旦实现了比较器,就说明这个类支持排序,比如单例模式的类就不能进行排序。
下面看看Comparable接口的源码(了解下就行)
package java.lang;
public interface Comparable<T>
{
public int compareTo(T o);
1)如果此对象(调用比较器方法的对象)大于指定对象(目标比较对象),返回正整数
2)如果此对象小于指定对象,返回负整数
3)如果此对象等于指定对象,返回零
}
外部比较器(简单点说就是把比较器写在类的外边,没错!就是在外边新定义了个比较器类!)的概念
新定义一个类,类名随意,但这个类必须实现Comparator接口,重写compare方法,我们把这个称作外部比较器。
下面看看Comparable接口的源码(了解下就行)
package java.util;
public interface Comparator<T> {
int compare(T o1, T o2);
1)如果o1大于o2,则返回正整数;
2)如果o1小于o2,则返回负整数
3)如果o1等于o2,则返回零
boolean equals(Object obj); //你是不是想问为什么这个方法不需要重写?对不起,我也不是特别清楚,但通常是把这个方法忽略掉,如果
必须要个说法,你可以说这个方法被默认实现了。
}
现在我们有两种比较器的实现方法,用的时候用哪种呢?
1.我们自定义一个类时,可以选择内部比较器,内部比较器很符合java封装的思想,也就是高内聚,但是!但是!但是!我们平时用到的类往往不是自定义的,而是别人已经写好并且已编译的类,我们只能调用,不能修改其源代码,这时我们就只能用外部比较器了。
2.还有种情况,我们用到的还是别人已经写好并且已编译的类,他写这个类的时候恰好也实现了内部比较器(我们常用的有基本类型的封装类,String,Date),但是他定义的这种比较方法,不是我们想要的(举个例子,integer的内部比较器是按照数字大小进行比较排序,但是我们的需求是按照数字的绝对值进行排序,这就很尴尬了),这时我们就只能用外部比较器了。
3.第三种情况,我们平时对对象进行排序,往往需要多种排序方式(举个例子,学生表的排序方式有学号排序,年龄排序,性别排序等等),这时我们也不得不用外部比较器了(也就是定义多个外部比较器类)。
由此看出,外部比较器比内部比较器更灵活,更易维护。
比较器的应用
最最常见的应用还是用在集合(list)和数组的sort()方法中,如果我们想用sort()方法,必须实现存储元素对象的内部比较器或者自定义一个用于存储元素对象之间的外部比较器,不然用sort()方法的时候容器会报错,它不知道用哪种方式进行排序。
下面贴个比较器和sort方法的应用Demo,自己体会一哈:
- /**
- * 员工实体
- * @author Sam
- *
- */
- public class Employee implements Comparable<Employee> {
- private int id;// 员工编号
- private double salary;// 员工薪资
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public double getSalary() {
- return salary;
- }
- public void setSalary(double salary) {
- this.salary = salary;
- }
- public Employee(int id, double salary) {
- super();
- this.id = id;
- this.salary = salary;
- }
- // 为了输出方便,重写toString方法
- @Override
- public String toString() {
- // 简单输出信息
- return "id:"+ id + ",salary=" + salary;
- }
- // 比较此对象与指定对象的顺序
- @Override
- public int compareTo(Employee o) {
- // 比较员工编号,如果此对象的编号大于、等于、小于指定对象,则返回1、0、-1
- int result = this.id > o.id ? 1 : (this.id == o.id ? 0 : -1);
- // 如果编号相等,则比较薪资
- if (result == 0) {
- // 比较员工薪资,如果此对象的薪资大于、等于、小于指定对象,则返回1、0、-1
- result = this.salary > o.salary ? 1 : (this.salary == o.salary ? 0 : -1);
- }
- return result;
- }
- }
- /**
- * 测试两种比较器
- * @author Sam
- *
- */
- public class TestEmployeeCompare {
- /**
- * @param args
- */
- public static void main(String[] args) {
- List<Employee> employees = new ArrayList<Employee>();
- employees.add(new Employee(2, 5000));
- employees.add(new Employee(1, 4500));
- employees.add(new Employee(4, 3500));
- employees.add(new Employee(5, 3000));
- employees.add(new Employee(4, 4000));
- // 内部比较器:要排序的对象要求实现了Comparable接口
- Collections.sort(employees);
- System.out.println("通过内部比较器实现:");
- System.out.println(employees);
- List<Employee> employees2 = new ArrayList<Employee>();
- employees2.add(new Employee(2, 5000));
- employees2.add(new Employee(1, 4500));
- employees2.add(new Employee(4, 3500));
- employees2.add(new Employee(5, 3000));
- employees2.add(new Employee(4, 4000));
- // 外部比较器:自定义类实现Comparator接口
- Collections.sort(employees2, new EmployeeComparable());
- System.out.println("通过外部比较器实现:");
- System.out.println(employees2);
- }
- }
- /**
- * 自定义员工比较器
- *
- */
- class EmployeeComparable implements Comparator<Employee> {
- @Override
- public int compare(Employee o1, Employee o2) {
- // 比较员工编号,如果此对象的编号大于、等于、小于指定对象,则返回1、0、-1
- int result = o1.getId() > o2.getId() ? 1 : (o1.getId() == o2.getId() ? 0 : -1);
- // 如果编号相等,则比较薪资
- if (result == 0) {
- // 比较员工薪资,如果此对象的薪资大于、等于、小于指定对象,则返回1、0、-1
- result = o1.getSalary() > o2.getSalary() ? 1 : (o1.getSalary() == o2.getSalary() ? 0 : -1);
- }
- return result;
- }
- }
你要是还不理解外部和内部比较器的区别,最后再送你个例子:
我和黄晓明谁比较帅?在我心里是不知道如何比较的,我不知道判断帅的标准是什么(这就相当于“我”这个实例,本身没有内部比较器),我现在很想很想知道答案,有两个方法,第一个是上网查资料,查判断帅的标准是啥,然后再比较(也就是实现内部比较器),第二个就是找一个裁判a,他知道判断标准是啥,让他来判断我们两个谁帅(相当于定义了一个外部比较器类a)