深入理解Comparable和Comparator

Comparable和Comparator区别

JDK version:1.8.0_171


本文先介绍Comparator和Comparable两个接口的作用并给出源码分析,最后分别给出实际案例

1. ComparableComparator简单介绍

  • 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方法,该方法传入一个泛型对象,标明所要比较的另一个对象,当该参数为空时抛出异常。
将当前对象与传入的对象进行比较,返回负整数,零,或正整数,分别表示当前对象小于,等于或大于传入对象。

  • Comparator

      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方法返回正数,零或负数表示第一个对象小于,等于或大于第二个对象。


2. 实际应用

  简单说来,任何实现了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来定制排序的方式

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务