Comparator链式排序需以comparing()开头,后接thenComparing()等方法;null值需用nullsLast/nulsFirst包装;数值字段降序用thenComparingDescending()或reverseOrder();Comparator仅定义规则,须配合sort()或sorted()才生效。

Comparator.comparing() 怎么链式拼多个字段?
直接用 thenComparing() 接续,不是反复调用 comparing()。很多人误以为要写成 comparing(a).comparing(b).comparing(c),这会编译失败——comparing() 返回的是 Comparator,它没有 comparing() 方法。
正确链路是:一个 comparing() 打头,后面全用 thenComparing()(或其变体)追加:
Comparator<user> cmp = Comparator.comparing(User::getAge)
.thenComparing(User::getName)
.thenComparing(User::getId);</user>
字符串字段排序时 null 值怎么不抛 NullPointerException?
comparing() 默认对 null 敏感,只要某个对象的比较字段为 null,运行时就抛 NullPointerException。这不是 bug,是设计行为。
解决方法是显式传入 null 安全的 Comparator,常用 Comparator.nullsLast(Comparator.naturalOrder()) 或 Comparator.nullsFirst(...):
Comparator.comparing(User::getName, Comparator.nullsLast(String::compareTo))Comparator.comparing(User::getEmail, Comparator.nullsFirst(Comparator.naturalOrder()))
注意:不能只写 Comparator.nullsLast(String::compareTo) ——String::compareTo 是方法引用,类型是 Comparator,而 nullsLast 需要这个 Comparator 作为参数,所以必须完整传递。
数值字段升序/降序混排怎么写?
thenComparing() 默认升序;要降序,得用 thenComparingDescending(),或者更通用的 thenComparing(Comparator.reverseOrder())。
例如:先按年龄升序,再按分数降序,最后按 id 升序:
Comparator<student> cmp = Comparator.comparing(Student::getAge)
.thenComparing(Student::getScore, Comparator.reverseOrder())
.thenComparing(Student::getId);</student>
关键点:
-
thenComparing(…, …)第二个参数是字段专属的Comparator,适合定制单字段逻辑(如忽略大小写、倒序、null 处理) -
thenComparingInt()/thenComparingLong()等原始类型特化方法性能略好,且自动处理null(前提是 getter 返回基本类型包装类且不为 null;若可能为 null,仍需配合nullsLast(Comparator.naturalOrder()))
为什么链式排序后集合没变?
Comparator 本身不修改数据,只是定义规则。你得把它传给实际排序操作的地方:
-
list.sort(cmp)(修改原 list) Collections.sort(list, cmp)-
list.stream().sorted(cmp).collect(Collectors.toList())(生成新 list)
常见疏忽:写了很长的 comparator,但忘记调用 sort() 或 sorted();或者误以为 Comparator 构建过程本身就会触发排序。
另外,如果对象字段是 final 或不可变类型(如 LocalDateTime),排序逻辑不会影响字段值,只影响它们在集合中的相对位置——这点容易被刚接触函数式排序的人忽略。










