Java中Comparable与Comparator的区别
Java 中的 Comparable 和 Comparator 都是比较有用的集合排序接口,但是这俩接口使用却有着明显区别,具体使用哪一个接口,今天我们来一起了解下。
Comparable 接口
Comparable 是一个排序接口,位于 java.lang 包下面,实现该接口的类就可以进行自然排序。先看下 Comparable 接口的定义:
package java.lang;
public interface Comparable<T> {
int compareTo(T);
}
实现 Comparable 接口时,需要实现 compareTo 方法,其中参数 T 为我们要比较的目标对象,compareTo 方法返回值决定了我们的排序顺序:
- 返回负值(-1 或更小):当前对象排目标对象前面,即正序排列
- 返回 0:当前对象和目标对象相等,排序不分先后
- 返回正值(1 或 更大):当前对象排目标对象后面,即倒序排列
下面来举个例子,首先我们定义一个 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 接口
Comparator 又被称为 Java 中的比较器,该接口位于 java.util 包下,定义如下:
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}
Comparator 接口标注 @FunctionalInterface 代表其是一个函数式接口,与 Comparable 不同,Comparator 可以选择性地允许比较空参数,同时保持传递比较特性的要求。
- compare 方法传入 2 个待比较的对象 o1、o2,假设返回值为负值(-1 或更小)代表 o1 排 o2 前面,反之 o1 排 o2 后面
- equals 方法传入一个 Object 对象,用于判断待比较的 2 个对象是否是一个对象,该接口不用实现,因为 Object 顶级类已经默认实现了
接下来我们使用 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 都能实现类排序,但是区别较大,主要有以下几方面:
- Comparable 是类的内部比较器,而 Comparator 是外部比较器;Comparable 比较只能有一种实现,但是 Comparator 可以根据具体业务要求变换多种比较规则,比较灵活
- Comparable 的排序规则只适用于该类的对象,而 Comparator 的排序规则可以适用于不同类型的对象
- 实现 Comparable 接口的类具有默认排序规则,不用使用时再去定义
日常使用的话,推荐自定义 Comparator 的方式。
作者:程序员小波与Bug
出处:https://codetrips.cn
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。如有问题,可以邮件:caiya928@aliyun.com
QQ:1419901425 联系我
如果喜欢我的文章,请关注我的公众号:程序员小波与Bug