JDK version:1.8.0_171
本文先介绍Comparator和Comparable两个接口的作用并给出源码分析,最后分别给出实际案例
实现Comparable接口的类都支持自然排序(文档中称为natural ordering),而接口中的compareTo方法则是说明了默认排序的实现(即可以通过重写该方法来定制新的排序方式)。 实现了Comparable接口的Lists(或arrays)对象可以直接通过Collections.sort(或Arrays.sort)进行排序。实现了该接口的对象能够充当sorted map中的键或者sorted set中的元素,而不需要指定comparator。
package java.lang; import java.util.*; public interface Comparable<T> { public int compareTo(T o); }Comparable接口中只存在一个compareTo方法,该方法传入一个泛型对象,标明所要比较的另一个对象,当该参数为空时抛出异常。 将当前对象与传入的对象进行比较,返回负整数,零,或正整数,分别表示当前对象小于,等于或大于传入对象。
Comparators能够充当一些排序方法的参数(比如Collections.sort或者Arrays.sort)来控制排序方式,Comparators也能用于对一些数据结构进行排序(比如sorted sets或者sorted maps),或给一些没有自然排序功能(即没有实现Comparable接口的)的Collections对象增加排序功能。
package java.util; import java.util.Comparators; @FunctionalInterface public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); ... }注解@FunctionalInterface标明该接口是一个函数式接口,支持lambda表达式或者作为方法引用的目标对象。 该接口主要有两个方法compare和equals,由于java中的类都继承了java.lang.Object,所以equals无需手动实现,因此我们只需要实现compare方法,而接口中剩余方法都提供了默认实现。 Comparator中的compare与Comparable中的compareTo方法类似,不过由于Comparator是一个函数式接口,因此其compare必须包含两个泛型参数,即所要比较的两个对象,compare方法返回正数,零或负数表示第一个对象小于,等于或大于第二个对象。
简单说来,任何实现了Comparable接口的类都支持排序,比如Java中的Integer、Character、String类,或实现了该接口的自定义类等,下面通过一个例子来说明。 现在有一个Employee类,已经实现了Comparable接口。
public class Employee implements Comparable<Employee>{ private int id; private String name; private int salary; /** * 薪资从低到高 * @param o 所要比较的对象 * @return */ @Override public int compareTo(Employee o) { return salary - o.salary; } }主函数如下所示:
public static void main(String[] args){ Employee employee1 = new Employee(1,"张三", 15000); Employee employee2 = new Employee(2,"李四", 18500); Employee employee3 = new Employee(3,"王五", 19000); Employee employee4 = new Employee(4,"陈六", 20000); List<Employee> employees = new ArrayList<>(); employees.add(employee4); employees.add(employee2); employees.add(employee3); employees.add(employee1); // Collections.sort(employees); for(Employee employee : employees){ System.out.println(employee); } }输出如下所示,此时按照添加元素的顺序输出 取消主函数中第13行代码注释后,此时按照薪资从低到高输出结果: 当一个类没有实现Comparable接口,此时不支持默认排序。在不改动类的定义,并想要给这个类添加排序功能的情况下,Comparator就能派上用场了,仍然通过Employee来举例(类的构造函数以及getter,setter均已省略)。
public class Employee { private int id; private String name; private int salary; @Override public String toString() { return "id: " + id + " 姓名:" + name + " 薪资:" + salary; } }由于没有实现Comparable接口,此时无法直接调用Collections.sort(employees)来实现排序的,我们可以定义一个比较器Comparator,然后作为sort方法的第2个参数。
public static void main(String[] args){ Employee employee1 = new Employee(1,"张三", 15000); Employee employee2 = new Employee(2,"李四", 18500); Employee employee3 = new Employee(3,"王五", 19000); Employee employee4 = new Employee(4,"陈六", 20000); Employee employee5 = new Employee(5,"钱八", 20000); Employee[] employees = new Employee[]{employee4,employee2,employee3,employee1,employee5}; Comparator<Employee> comparator = new Comparator<Employee>() { @Override public int compare(Employee o1, Employee o2) { return o1.getSalary() - o2.getSalary(); } }; //传入比较器comparator Arrays.sort(employees, comparator); for(Employee employee : employees){ System.out.println(employee); } }效果与类实现Comparable接口是一样的 除了在Collections.sort和Arrays.sort中使用这两个接口,还可以在sorted set和sorted map中发挥作用。 下面给出在sorted map中使用comparator的一个例子,SortedMap是集合框架中的一个接口。此接口扩展了Map 接口并提供了其元素的总排序(元素可以按键的排序顺序遍历)。而TreeMap则实现了SortedMap接口。
public static void main(String[] args) { Employee employee1 = new Employee(1,"张三", 15000); Employee employee2 = new Employee(2,"李四", 18500); Employee employee3 = new Employee(3,"王五", 19000); Employee employee4 = new Employee(4,"陈六", 20000); Employee employee5 = new Employee(5,"钱八", 20000); SortedMap<String, Employee> sortedMap = new TreeMap<>(); sortedMap.put("b", employee3); sortedMap.put("c", employee2); sortedMap.put("a", employee5); sortedMap.put("e", employee1); sortedMap.put("d", employee4); // 此时sortedMap默认按照key值从小到大排序 for(Map.Entry<String, Employee> entry: sortedMap.entrySet()){ System.out.println(entry.getKey() + " :{" + entry.getValue() + "}"); } System.out.println("Comparator:" + sortedMap.comparator()); }SortedMap默认是按照key进行排序的,我们在自定义comparator的时候也是根据id来的,但如果我们非要让它按照map中的值,比如说我非要根据雇员的id来进行排序(个人不建议这么做,也可能是下面的方法有问题)。 首先是定义一个map,包含了所有数据,然后是将这个map作为构造函数参数,初始化自定义的Comparator,最后将这个comparator传入sorted map中,然而此时的sortedMap仅仅只是初始化了,是没有任何数据的,而且只能添加上面map中已经有的数据
public static void main(String[] args) { Employee employee1 = new Employee(1,"张三", 15000); Employee employee2 = new Employee(2,"李四", 18500); Employee employee3 = new Employee(3,"王五", 19000); Employee employee4 = new Employee(4,"陈六", 20000); Employee employee5 = new Employee(5,"钱八", 25000); Map<String, Employee> map = new HashMap<>(); map.put("b", employee3); map.put("c", employee2); map.put("a", employee4); map.put("d", employee1); Comparator<String> comparator = new MyComparator<String>(map); SortedMap<String, Employee> sortedMap = new TreeMap<String, Employee>(comparator); // 拷贝数据 sortedMap.putAll(map); for(Map.Entry<String, Employee> entry: sortedMap.entrySet()){ System.out.println(entry.getKey() + " :{" + entry.getValue() + "}"); } System.out.println("Comparator:" + sortedMap.comparator()); }自定义的Comparator代码如下,其中的Employee类已经实现了Comparable接口(根据id进行比较):
public class MyComparator<K> implements Comparator<K> { private Map<K, Employee> map; public MyComparator(Map<K, Employee> map) { this.map = map; } @Override public int compare(K k1, K k2) { return map.get(k1).compareTo(map.get(k2)); } }按照map中value的值id进行排序结果如下:
Comparable是“内部”比较器,而Comparator是“外部”比较器,这里的内部和外部是指是否修改类本身的源码。当一个类内部实现了Comparable接口时,表明这个支持“自然排序”,此外还可以(从类外部)构造Comparator来定制排序的方式