Java Comparator 和 Comparable 接口使用和区别

512 阅读3分钟

前言

本篇博客介绍 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