Comparable接口用于定义类的唯一自然排序,需在类内部实现compareTo方法;Comparator是外部灵活排序工具,支持链式调用与null安全处理,适用于不可修改类或多种排序策略。

Comparable 接口必须在类内部实现
如果一个类天然具有“自然顺序”,比如 String 按字典序、Integer 按数值大小,就该让这个类实现 Comparable 接口。它只有一个方法:compareTo(T o),返回负数、0 或正数,分别表示“小于”“等于”“大于”当前对象。
关键限制:一个类只能有一个 compareTo 实现,也就是说,Comparable 只能定义一种默认排序逻辑。你不能靠它支持姓名升序、年龄降序、部门分组等多种策略。
常见错误是把 compareTo 写成布尔返回值,或在比较前没处理 null —— compareTo 方法里如果传入 null,多数 JDK 实现会直接抛 NullPointerException,不是静默跳过。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 优先用
Objects.compare(a, b, Comparator.naturalOrder())代替手写 null 判断和类型转换 - 多个字段组合比较时,用
Comparator.comparing(...).thenComparing(...)链式调用更安全(见下节) - 若类已发布且无法修改源码(如第三方库的
User类),就别硬塞Comparable,改用Comparator
Comparator 是外部定义的灵活排序工具
Comparator 是函数式接口,核心方法是 compare(T o1, T o2)。它不侵入业务类,所有排序逻辑都写在外面,随时可换。
Java 8 起,Comparator 提供大量静态工厂方法,大幅降低手写逻辑成本:
-
Comparator.comparing(User::getName)→ 按姓名升序 -
Comparator.comparing(User::getAge).reversed()→ 按年龄降序 -
Comparator.comparing(User::getDept).thenComparing(User::getSalary)→ 先按部门,部门相同时再按薪资
注意:这些方法返回的是不可变的 Comparator 实例,线程安全;但如果你用 lambda 捕获了可变变量(比如一个动态配置的排序字段名),那就得自己保证线程安全。
容易踩的坑:
- 链式调用中某一步抛出
NullPointerException(例如user.getName()返回null),整个比较就崩了。可用comparing(..., Comparator.nullsLast(String::compareTo))显式处理 - 在
TreeSet或TreeMap中传入Comparator时,必须确保其满足“一致性”:对同一对对象多次调用compare必须返回相同结果;否则集合行为未定义
Arrays.sort() 和 Collections.sort() 怎么选参数
Arrays.sort() 有两个常用重载:
-
Arrays.sort(T[] a):要求T实现Comparable,否则编译失败 -
Arrays.sort(T[] a, Comparator super T> c):无视类是否实现Comparable,强制用你给的Comparator
Collections.sort() 同理,但只接受 List。它底层其实也是转成数组再调 Arrays.sort()。
性能上没本质差异,但要注意:
- 对基本类型数组(如
int[]),只能用无参版Arrays.sort(int[]),没有Comparator版本 —— 因为基本类型不能泛型,Comparator只作用于引用类型 - 如果你传了一个不稳定的
Comparator(比如依赖当前时间或随机数),Arrays.sort()在 JDK 7+ 使用双轴快排,可能因比较结果不一致而抛IllegalArgumentException: Comparison method violates its general contract!
什么时候该用 Comparable,什么时候必须用 Comparator
判断依据不是“哪个高级”,而是“谁拥有排序意图”:
- 类的设计者认为“这个类型天生就该这么排”,比如
LocalDateTime按时间先后,就实现Comparable - 调用方临时需要不同视角排序(如管理员看注册时间、客服看最后登录时间、报表按地域分组),一律用
Comparator - 要对
final类或第三方类排序,且不能继承/代理(比如java.time.LocalDate已是 final),只能用Comparator
真正容易被忽略的一点:Comparable 的实现一旦上线,就是 API 的一部分。后续想改排序逻辑(比如从“按创建时间”变成“按更新时间”),就属于不兼容变更 —— 所有依赖自然序的地方都会悄然改变行为。而 Comparator 是局部变量或方法参数,影响范围可控。










