Java中易混淆的两个接口:Comparable和Comparator

Comparable接口定义

Comparable是排序接口,如果一个类实现了Comparable接口,意味着这个类是支持排序的,即实现这个类的对象的List或Array可以通过Collections.sort或Arrays.sort进行排序。

Comparable接口来自java.lang包,它仅仅只有一个方法:

1
2
3
4
5
package java.lang;

public interface Comparable<T> {
int compareTo(T var1);
}

这个方法的意思是,假设我们使用x.compareTo(y)来比较xy的大小,若返回负数,意味着x小于y;若返回正数,意味着x大于y;若返回0,意味着两者相等。

Comparator接口定义

Comparator是比较器接口,在我们需要控制某个没有实现Comparable接口的类的次序时使用。

Comparator接口在Java8中进行了较大的改进,在Java8之前Comparator接口也仅只有两个函数:compare()和equals(),但在Java8中达到了18个方法。不过,其余的方法都是接口的default方法以及static静态方法,所以并不需要在实现时额外实现。对于equals方法,其实也不需要额外的实现。原因是所有类都继承自java.lang.Object这个类,而Object中已经实现了equals方法,那么我们的这个匿名内部类自然不需要实现equals方法。所以在实现Comparator接口时,必须实现的只有Compare方法。

Java中比较器升序降序的实现

对于compare方法,它与compareTo方法类似。在自定义比较器时,总是将升序降序弄混,有必要总结一下。

1
public int compare(Customer o1, Customer o2) {}

这个函数有两个参数用于比较:

  • 当返回负数时,表示不需要交换o1和o2的位置,依旧是o1排在o2前面,升序。
  • 当返回正数时,表示需要交换o1和o2的位置,o2排在o1前面,降序。

下面用一段实现Comparator用作匿名内部类的代码作为举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import java.util.*;

public class Customer {
private String name;
private int age;

public Customer (String name, int age) {
this.age = age;
this.name = name;
}

public int getAge() {
return age;
}

public String getName() {
return name;
}

public void setAge(int age) {
this.age = age;
}

public void setName(String name) {
this.name = name;
}

public static void main(String[] args) {
Customer customer1 = new Customer("James", 23);
Customer customer2 = new Customer("Durante", 35);
List<Customer> list = new ArrayList<>();
list.add(customer1);
list.add(customer2);

list.sort(new Comparator<Customer>() {
@Override
public int compare(Customer o1, Customer o2) {
// 若返回负数,表示不需要交换o1和o2的位置
// return o1.age - o2.age;
// 若返回正数,表示需要交换o1和o2的位置
return o2.age - o1.age;
}
});
for (Customer customer : list)
System.out.println("name: " + customer.getName() + " age: " + customer.getAge());
}
}

得到的结果为:

name: Durante age: 35
name: James age: 23

解释一下,这个函数中的重写的compare方法用于实现降序:

  1. 在这个函数中,若o1.age > o2.age,那么o2.age - o1.age < 0,返回负数,不需要交换o1和o2的顺序,降序

  2. 同样,若o1.age < o2.age,那么o2.age - o1.age > 0,返回正数,需要不交换o1和o2的顺序,还是降序。

总结

Java中有两种排序方式:

  1. Comparable 是自然排序。(在实体类中实现)
  2. Comparator 是定制排序。(不修改实体类,直接在调用方创建)

一些普通数据类型已经实现了Comparable接口,如String, Integer, Double等,我们可以直接调用。而对于一些我们自定义的类,在不同的情况下可能需要不同的比较策略,不能在类中将Comparable定死,这时候通常在调用方创建Comparator进行定制排序。

over~