WHY:当需要对某个类进行排序,但是这个类本身并不支持排序时,可以用到比较器,java中定义了两种比较器的类型,第一种是comparator可用于数组或者集合的排序,第二种是comparable可用于数组的排序。
首先来讲一下comparator比较器
场景1
需要对单位的员工信息列表进行排序,要求如下:
1、级别高的排在前面
2、同级别的,工资高的排在前面
3、同工资的,资历老的排在前面
STEP ONE:创建Employee类
public class Employee implements Serializable { private static final long serialVersionUID = 4775629632953317597L; public int id; public int level; public int salary; public int year; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getLevel() { return level; } public void setLevel(int level) { this.level = level; } public int getSalary() { return salary; } public void setSalary(int salary) { this.salary = salary; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public Employee(int id, int level, int salary, int year) { this.id = id; this.level = level; this.salary = salary; this.year = year; } }
STEP TWO:创建Comparator比较器
public class EmpComparator implements Comparator<Employee> { @Override public int compare(Employee employee1, Employee employee2) { int cr = 0; //按级别降序排列 int a = employee2.getLevel() - employee1.getLevel(); if (a != 0) { cr = (a > 0) ? 3 : -1; } else { //按薪水降序排列 a = employee2.getSalary() - employee1.getSalary(); if (a != 0) { cr = (a > 0) ? 2 : -2; } else { //按入职年数降序排列 a = employee2.getYear() - employee1.getYear(); if (a != 0) { cr = (a > 0) ? 1 : -3; } } } return cr; } }
STEP THREE:创建单元测试主类,测试排序结果
public class SortTest { public static void main(String[] args) { List<Employee> employeeList = new ArrayList<Employee>() { { add(new Employee(1, 9, 10000, 10)); add(new Employee(2, 9, 12000, 7)); add(new Employee(3, 5, 10000, 12)); add(new Employee(4, 5, 10000, 6)); add(new Employee(5, 3, 5000, 3)); add(new Employee(6, 1, 2500, 1)); add(new Employee(7, 5, 8000, 10)); add(new Employee(8, 3, 8000, 2)); add(new Employee(9, 1, 3000, 5)); add(new Employee(10, 1, 2500, 4)); add(new Employee(11, 2, 2000, 4)); } }; Collections.sort(employeeList, new EmpComparator()); System.out.println("ID\tLevel\tSalary\tYears"); System.out.println("============================="); for (Employee employee : employeeList) { System.out.printf("%d\t%d\t%d\t%d\n", employee.getId(), employee.getLevel(), employee.getSalary(), employee.getYear()); } System.out.println("============================="); } }
以上代码就完整的演示了使用比较器接口Comparator,对本身不支持排序的实体类进行排序的整个过程,接着我们来介绍一下java.util.Comparator接口的工作原理:
Comparator接口的源代码是:
public interface Comparator{
int compare(T o1,T o2);
boolean equals(Object obj) ;
}
当一个类要实现Comparator接口时,它必须要实现compare函数,而equals函数则可以不实现
本身的话,int compare(T o1,To2)函数是比较o1,o2的大小,
如果返回负值则意味着o1比o2小;否则o1等于o2;返回正值则意味着o1比o2大
但是在Comparator的实现类中要重写compare方法
在Comparator比较器中升降序可以理解为:
如果o1比o2大,若返回重写后的compare方法返回的是正数,此时调用的函数Collection.sort()就是升序,若返回的是负数,此时调用的函数Collection.sort()就是降序
同理如果o1比o2小,若返回的是负数,此时调用的函数Collection.sort()就是升序,若返回的是正数,此时调用的函数Collection.sort()就是降序
总结一句就是说,重写后的compare方法,返回的值若与compare(T o1,T o2)函数同正负号则是升序排列,若正负号相反则降序排列
public class EmpComparator implments Comparator<Employee>{ public int compare(Employee e1,Employee e2){ if(e1.getLevel()>e2.getLevel()) {return -1;} } }
像如上所示的代码,e1的级别大于e2的级别,但是在Comparator接口的实现类EmpComparator的重写的compare方法中却返回的是负数,因此EmpComparator实现的是雇员按照级别降序排列
场景2
java的比较器有两类,一种是comparator在场景一中已经详细描述了,另一种是comparable接口,我们现在来讲第二种。
先说一下为什么有两种比较器呢,我们先来看一下comparable接口是如何工作的:
让需要排序的类实现comparable接口,在其中重写compareTo(T o)方法,并在其中定义排序规则,就可以直接用java.util.Arrays.util来排列对象
用comparable的好处在于,直接在对象中就定义了排序的规则,不用再重新重新写比较器的实现类
class Student implements Comparable<Student>{ private String name; private int age; private float score; public Student(String name, int age, float score) { this.name = name; this.age = age; this.score = score; } public String toString() { return name+"\t\t"+age+"\t\t"+score; } @Override public int compareTo(Student o) { // TODO Auto-generated method stub if(this.score>o.score)//score是private的,为什么能够直接调用,这是因为在Student类内部 return -1;//由高到底排序 else if(this.score<o.score) return 1; else{ if(this.age>o.age) return 1;//由底到高排序 else if(this.age<o.age) return -1; else return 0; } } } public class ComparableDemo01 { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Student stu[]={new Student("zhangsan",20,90.0f), new Student("lisi",22,90.0f), new Student("wangwu",20,99.0f), new Student("sunliu",22,100.0f)}; java.util.Arrays.sort(stu); for(Student s:stu) { System.out.println(s); } } }
看完了comparable比较器之后我们来说一下为什么有两种比较器:
因为有些类在设计的时候没有考虑让其实现comparable接口,因此才有了comparator比较器用以补救。
场景3
我们看到,需要排列的对象,有时候是装在数组里,有时候是装在集合里,相应的也就需要调用不同的排序方法,
当对象装在数组中时
如果是用的comparator比较器,则调用Arrays.sort()方法,需要传递两个参数,(数组名 new EmpComparator())
如果用的是comparable比较器,则调用Arrays.sort()方法,需要传递一个参数,(数组名)
当对象装在集合中时
则只能调用Collection.sort()方法,需要传递两个参数(集合名 new EmpComparator()),即对象和比较器,这也就意味着集合只能用comparator比较器来实现对象的排序。