Comparable 和 Comparator 的原理及适用场景浅析

1,914 阅读3分钟

Comparable

某个类只要实现了 Comparable 接口就能够让自身拥有可以被排序的能力,排序能力是通过 compareTo(Object obj) 这个方法来实现的。

基本类型排序

一个基本类型要参与排序自然是用当前的值和需要比较的值进行比对排序

    static void test() {
        List<Integer> list = new ArrayList<>();
        list.add(10);
        list.add(8);
        list.add(9);
        list.add(1);
        list.add(2);
        Collections.sort(list);
        list.forEach(System.out::println);
    }

Integer 也实现了 Comparable 接口

    public int compareTo(Integer anotherInteger) {
        return compare(this.value, anotherInteger.value);
    }

自定义对象排序

那么当我们要对自定义对象进行排序的时候,该如何确定顺序呢。

class Student {

    int age;

    int number; // 学号

    public Student(int age, int number) {
        this.age = age;
        this.number = number;
    }
}

假如说按照年龄排序

class Student implements Comparable<Student> {

    int age;

    int number; // 学号

    public Student(int age, int number) {
        this.age = age;
        this.number = number;
    }

    @Override
    public int compareTo(Student o) {
        return this.age - o.age;
    }
}

比如说对一系列数据进行动态排序,现目前已经有学生年龄为 15, 16, 19 排好顺序了,现在新加入数据 17 要对年龄为 17 的进行排序

  1. 首先 this.age - o.age = 16 - 15 > 0 那么能够确定数据在 15 后面
  2. 然后 this.age - o.age = 17 - 16 > 0 那么能够确定数据在 16 后面
  3. 然后 this.age - o.age = 17 - 19 < 0 那么最终确定位置数据在 19 前面

在这种实现算法上可以考虑使用二分查找的方来快速确定插入的位置进行排序

查看 Collections.sort() 源码它底层的实现排序的方式正是通过二分查找来进行快速找到对应的位置插入来满足排序的

Comparator

如果说 Comparable 是可以让对象自身拥有可以排序的能力的话,那么 Comparator 就是让两个自身没有排序能力的对象可以进行根据 Comparator 定义的规则进行排序

比如说某个类 Project 没有实现 Comparable 接口,也不允许我们修改这个类的代码,但是这个时候我们需要根据它的 id 进行排序,那么就可以使用 Comparator 了

class T {
    Long id;

    String title;

    public Project(Long id, String title) {
        this.id = id;
        this.title = title;
    }
}

class Test {
    void test() {
        List<Project> list = new ArrayList<>();
        list.add(new Project(10L, "A"));
        list.add(new Project(8L, "B"));
        list.add(new Project(12L, "C"));
        list.add(new Project(19L, "D"));
        list.add(new Project(13L, "E"));
        Collections.sort(list, new Comparator<Project>() {
            @Override
            public int compare(Project o1, Project o2) {
                return o1.id.compareTo(o2.id);
            }
        });
        list.forEach(System.out::println);
    }
}

可以看到使用 Comparator 更加灵活,比如 Comparable 限制了我们只能使用某个属性排序比如 id,就不能再做调整,而 Comparator 比较灵活这里除了使用 id 排序我们还可以使用 title 进行排序

class Test {
    void test() {
        List<Project> list = new ArrayList<>();
        list.add(new Project(10L, "A"));
        list.add(new Project(8L, "B"));
        list.add(new Project(12L, "C"));
        list.add(new Project(19L, "D"));
        list.add(new Project(13L, "E"));
        Collections.sort(list, new Comparator<Project>() {
            @Override
            public int compare(Project o1, Project o2) {
                return o1.title.compareTo(o2.title);
            }
        });
        list.forEach(System.out::println);
    }
}

Comparable 和 Comparator 的相同和异同点

  • 他们都是用来排序的,都可以使用二分查找等方式来快速的将值插入到指定位置满足排序需求
  • 实现了 Comparable 接口对象就具有了排序的能力,很多具备排序能力的框架在对对象进行排序的时候,首先就回去判断对象是否实现了 Comparable 接口,如果没有的话就需要定义 Comparator
  • Comparator 更加灵活,可以将不具备排序能力的对象进行排序
  • 当然我们也可以不使用 Comparable 和 Comparator 自己通过遍历排序等方式确定各自的顺序