前言
本篇博客介绍 Java 中集合元素进行比较时使用的两种接口:
- Comparable接口,自然排序接口
- 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