Collections.sort 依赖 Comparator 指定排序规则,关键在于明确比较维度、顺序与边界,优先使用内置安全方法、null 包装器和链式 thenComparing,并确保满足自反性、对称性、传递性。

Collections.sort 是 Java 中对 List 进行就地排序的常用方法,它本身不直接定义排序规则,而是依赖 Comparator 接口来指定“怎么比”。写好自定义排序逻辑,关键不是堆砌代码,而是想清楚比较的维度、顺序和边界。
明确比较目标:先想清楚“比什么”
排序前必须确定依据字段(如年龄、姓名、时间戳)以及升序还是降序。多个字段需考虑优先级,比如“先按部门升序,部门相同时按薪资降序”。避免临时起意写比较逻辑,否则容易漏掉 null 处理或类型不一致问题。
- 基础字段比较用
Integer.compare(a, b)、String.compareTo()等内置安全方法,别直接用a - b(可能溢出)或a.equals(b)(返回布尔,无法表达大小关系) - 涉及 null 值时,优先用
Comparator.nullsFirst()或Comparator.nullsLast()包装,而不是手动 if-else 判断 - 时间比较推荐用
LocalDateTime.compareTo(),不要转成毫秒再减法
链式组合多条件:用 thenComparing 比嵌套 if 更清晰
多个排序条件不必层层 if-else 判断,Java 8+ 提供了流畅的链式写法,可读性高且不易出错。
- 单字段升序:
Comparator.comparing(Person::getAge) - 单字段降序:
Comparator.comparing(Person::getSalary).reversed() - 多字段组合:
Comparator.comparing(Person::getDept).thenComparing(Person::getSalary).reversed()(注意 reversed() 作用于整个链) - 带 null 安全的多字段:
Comparator.comparing(Person::getNickName, Comparator.nullsLast(String::compareTo)).thenComparing(Person::getId)
避免常见陷阱:null、类型、可变对象
看似简单的 compare 方法,实际运行中常因这几个点抛出异常或结果错乱。
- Comparator 必须满足自反性(
compare(a,a)==0)、对称性(compare(a,b)和compare(b,a)符号相反)、传递性。手动写逻辑时建议用 IDE 自动生成或严格测试边界值 - 不要在 compare 中修改对象状态(比如调用 setter),排序过程可能多次调用 compare,副作用会导致不可预测行为
- 若 List 中元素类型不统一(如混入 null 或子类实例),确保 compare 方法能兼容,或提前过滤/转换
- 对浮点数比较慎用 ==,应使用
Double.compare(a, b)避免 NaN 和精度问题
Lambda vs 方法引用:选最简但不失可读的方式
小逻辑用 lambda 更紧凑,复杂逻辑建议抽成独立方法并命名,方便复用和单元测试。
- 简单场景:
Collections.sort(list, (a, b) -> Integer.compare(a.getScore(), b.getScore())) - 可读性优先:
Collections.sort(list, comparingByScoreThenName()),其中comparingByScoreThenName()返回预定义好的 Comparator - 需要复用或测试时,把 Comparator 写成静态工具方法,例如
PersonComparators.byAgeDesc()
真正让排序稳定可靠的,不是语法有多炫,而是每一步比较都符合业务语义、处理了现实数据中的各种“不干净”。写完记得用含 null、重复值、边界值的样例跑一遍验证。

















