Java 中的 Comparable 和 Comparator 都是比较有用的集合排序接口,但是这俩接口使用却有着明显区别,具体使用哪一个接口,今天我们来一起了解下。
Comparable 是一个排序接口,位于 java.lang 包下面,实现该接口的类就可以进行自然排序。先看下 Comparable 接口的定义:
package java.lang;
public interface Comparable<T> {
int compareTo(T);
}
实现 Comparable 接口时,需要实现 compareTo 方法,其中参数 T 为我们要比较的目标对象,compareTo 方法返回值决定了我们的排序顺序:
下面来举个例子,首先我们定义一个 User 对象:
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class User implements Comparable<User> {
private String username; // 用户名
private Integer age; // 年龄
@Override
public int compareTo(User nextUser) {
// 这里我们先直接使用 Integer 自带的 compareTo 进行比较
return this.age.compareTo(nextUser.getAge());
}
}
我们来测试下:
public static void main(String[] args) {
List<User> userList = new ArrayList<>();
userList.add(User.builder().age(11).username("张三").build());
userList.add(User.builder().age(9).username("李四").build());
Collections.sort(userList);
log.info("userList: {}", userList);
// userList: [User(username=李四, age=9), User(username=张三, age=11)]
}
在 User 对象我们实现了 compareTo 方法,使用 Integer 自带的 compareTo 进行比较,我们看下 Integer 的 compareTo 是怎么实现的:
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
可以看到,当 x 比 y 小时,返回 -1,即 x 排在 y 前面(根据我们的返回值规则,返回 -1 时:当前对象排目标对象前面);当 x > y 时,返回 1,即 x 排后面,总结下就是 小的值排前大的值排后,所以 Integer 自带的 compareTo 其实就是数值按照从小到大排列,即正排序。
所以 this.age.compareTo(nextUser.getAge()) 其实就是根据 User 的 age 大小进行正排序,小的排前面大的排后面。那么假设有一天我们不想根据 age 排序了,我们想换成根据 username 自然排序,怎么办?如果一股脑调整 User 的 compareTo 逻辑怕是不能应对需求的变动吧,这时候就要用到 Java 中的比较器 Comparator 了。
注意:在实现 Java Comparable 接口时,Java 要求实现必须遵循以下传递比较特性:如果对象 A 大于对象 B,对象 B 大于 对象 C,那么对象 A 也一定是大于对象 C 的。
Comparator 又被称为 Java 中的比较器,该接口位于 java.util 包下,定义如下:
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}
Comparator 接口标注 @FunctionalInterface 代表其是一个函数式接口,与 Comparable 不同,Comparator 可以选择性地允许比较空参数,同时保持传递比较特性的要求。
接下来我们使用 Comparator 实现根据 age 从小到大排序的功能:
Collections.sort(userList, (o1, o2) -> {
return o1.getAge().compareTo(o2.getAge());
});
log.info("userList: {}", userList);
// userList: [User(username=李四, age=9), User(username=张三, age=11)]
同样的,我们想根据 username 排序的话:
Collections.sort(userList, (o1, o2) -> {
return o1.getUsername().compareTo(o2.getUsername());
});
log.info("userList: {}", userList);
// [User(username=张三, age=11), User(username=李四, age=9)]
可能大家会疑惑为啥 “张三” 还排在 “李四” 前面了,因为汉字 compareTo 时,默认是基于字符串中每个字符的 Unicode 值进行比较的,然后按照字典排序进行排列,字典排序靠前的自然排前面了。
Comparable 和 Comparator 都能实现类排序,但是区别较大,主要有以下几方面:
日常使用的话,推荐自定义 Comparator 的方式。