前言

本篇博客介绍 Java 中集合元素进行比较时使用的两种接口:

  1. Comparable接口,自然排序接口
  2. Comparator接口,自定义排序接口

Comparable可以看作一个内部比较器,通常选择在定义类时让类实现该接口。

Comparator可以看作一个外部比较器,在需要的时候再定义比较规则。

Comparable 接口介绍

如果一个类实现了Comparable接口,则需要实现compareTo方法。实现后,在排序时这个类则可以按照compareTo定义的规则进行排序,无需额外指定比较器。

compareTo方法规定:

  • 如果返回值为负数,则被比较对象小于比较对象
  • 如果返回值为零,则被比较对象等于比较对象
  • 如果返回值为正数,则被比较对象大于比较对象
public interface Comparable<T> {
    public int compareTo(T o);
}

泛型T表示比较对象的类型,通常T都是实现该接口的类本身。

现在这里有一个实现了Comparable接口的示例代码:

public class Person implements Comparable<Person> {
    private String personName;
    private Integer personAge;

    public Person() {
    }

    public Person(String personName, Integer personAge) {
        this.personName = personName;
        this.personAge = personAge;
    }

    public String getPersonName() {
        return personName;
    }

    public void setPersonName(String personName) {
        this.personName = personName;
    }

    public Integer getPersonAge() {
        return personAge;
    }

    public void setPersonAge(Integer personAge) {
        this.personAge = personAge;
    }

    @Override
    public int compareTo(Person o) {
        return this.personAge.compareTo(o.getPersonAge());
    }
}

当Person类需要排序时会调用其内部的compareTo方法,这里通过比较personAge进行排序。

甚至,我们可以使用对象的hashCode进行排序,但是在实际的业务场景中没有意义。通常对象的hashCode值会用在HashMap、TreeMap等比较中。

Comparator 接口介绍

如果一个POJO类需要需要一个排序规则,但是又没有实现Comparable接口,那么此时有两种选择:

  • 在POJO类上实现Comparable接口,并实现compareTo方法
  • 实现Comparator接口,定义一个比较器

又或者如果一个POJO类已经实现Comparable接口,但是compareTo的比较规则不适用于当前代码的场景,那么此时就需要Comparator接口了。

此时Comparator接口的优势就显现出来了,它不需要修改POJO的源代码。实际上,Comparator接口的实现是策略模式的体现。

Comparator接口部分定义:

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
}

Comparator接口是一个函数式接口,除了compare方法外还有很多其他方法,这里暂不做介绍。

compare方法规定:

  • 如果返回值为负数,则o1对象小于o2
  • 如果返回值为零,则被o1等于o2
  • 如果返回值为正数,则被o1大于o2

Comparator接口示例代码:

public static void main(String[] args) {
  List<Person> people = new ArrayList<>();
  Person p1 = new Person();
  p1.setPersonAge(1);

  Person p2 = new Person();
  p2.setPersonAge(4);
  
  Person p3 = new Person();
  p3.setPersonAge(2);

  people.add(p1);
  people.add(p2);
  people.add(p3);

  people.sort(new Comparator<Person>() {
    @Override
    public int compare(Person o1, Person o2) {
      return o1.getPersonAge() - o2.getPersonAge();
    }
  });
}

代码中排序使用了List内部的sort方法。使用sort方法时,如果Person没有实现Comparable接口,则必须指定一个比较器,此时就需要一个实现Comparator接口的比较器类。

除了上面实现比较器的方式外,还有其他方式,尤其是从Java 8开始支持函数式后,实现一个比较器就不需要像上面的代码一样繁琐了:

//1. 使用Comarator的comparing方法
people.sort(Comparator.comparing(Person::getPersonAge));

//2. Lambda表达式
people.sort((person1, person2) -> person1.getPersonAge() - person2.getPersonAge());

//3. Collections.sort
Collections.sort(people, (person1, person2) -> person1.getPersonAge() - person2.getPersonAge());

Collections.sort(people, Comparator.comparingInt(Person::getPersonAge));

以上几种方式都使用了函数式的方式,他们可以让代码更加简练和容易理解。

以上几种集合排序方法的调用链:

Collections.sort -> list.sort -> Arrays.sort -> Arrays.mergeSort