Lambda写Comparator的核心是利用函数式接口特性,将匿名类压缩为一行安全可复用表达式;推荐用comparing系列静态方法提升语义清晰度与健壮性,并通过thenComparing链式实现多级排序。

用Lambda表达式写Comparator,核心就一条:利用它作为函数式接口的特性,把原来几行匿名内部类压缩成一行可读、安全、可复用的表达式。
基础写法:从匿名类到Lambda一步到位
以前写字符串按长度排序,得这样:
- 新建匿名内部类,重写 compare 方法
- 手动比较 length(),再处理返回值逻辑(比如 return s1.length() - s2.length())
现在直接写:
(s1, s2) -> Integer.compare(s1.length(), s2.length())注意两点:用 Integer.compare() 替代减法,避免整数溢出;参数类型省略,编译器自动推导。
推荐写法:用 comparing() 系列静态方法
Java 8 提供了更高级的抽象,让语义更清晰、代码更健壮:
- Comparator.comparing(String::length) —— 默认升序,底层调用 compareTo()
- Comparator.comparingInt(String::length) —— 专用于 int 类型提取,性能更好、不装箱
- Comparator.comparing(String::length).reversed() —— 快速取反,实现降序
这些方法内部仍是 Lambda,但封装了空值检查、类型约束和契约保障,比手写更可靠。
复合排序:thenComparing 链式组合
单一字段不够?比如先按年龄升序,年龄相同时按姓名字典序:
- Comparator.comparingInt(Person::getAge).thenComparing(Person::getName)
- 支持多级嵌套:
.thenComparing(...).thenComparing(...) - 每个环节都可独立指定升/降序,例如
.thenComparing(Comparator.reverseOrder())
这种写法天然满足全序性要求,避免 TreeMap 或 TreeSet 中因返回 0 导致元素被覆盖的问题。
避坑要点:别让简洁牺牲正确性
看似简单的 Lambda,容易踩几个隐性陷阱:
- 别写 (a, b) -> a.length() - b.length():大长度字符串相减会溢出,结果不可靠
- 别在比较逻辑里返回固定 0(如长度相等就 return 0):这会让不同对象被视为“相等”,TreeMap 会丢数据
- 涉及 null 值时,优先用 Comparator.nullsFirst() 或 nullsLast() 包裹,而不是自己判空
真正简洁的代码,是逻辑完整、边界清晰、别人一眼能看懂意图的那一种。










