Comparator 与 Lambda 表达式结合使用

摘要:Comparator作为函数式接口,与Lambda表达式结合可简化排序代码。传统匿名内部类冗长,Lambda只需核心比较逻辑,如`students.sort((s1, s2) -> s2.getAge() - s1.getAge())`。Java 8的`Comparator.comparing()`支持方法引用、链式排序(`.thenComparing()`)、降序(`.reversed()`)及null值处理(`nullsFirst/Last`),使代码更简洁安全。

作者头像
LumiBee
7 天前 · 21 0
分享

Comparator 与 Lambda 结合

1. 天作之合

原因:Comparator 是一个函数式接口 (Functional Interface,如果一个接口只要有且仅有一个抽象方法,它就是函数式接口)。

Comparator 的核心方法正是 int compare(T o1, T o2)

Lambda 表达式的本质就是对函数式接口的匿名实现。因此,我们不再需要写一个完整的匿名内部类,只需要提供与compare 方法签名匹配的一小段代码即可。

2. 从传统到 Lambda 的演变

这里通过定义一个 Student 类作为例子。

public class Student {
    private String name;
    private int age;
    // 构造函数、getter、toString 省略...
}

假设我们要按年龄降序排序。

  1. 传统方式:匿名内部类

    List<Student> students = ...;
    
    Collections.sort(students, new Comparator<Student>() {
        @Override
        public int compare(Student s1, Student s2) {
            // 降序:s2 在前
            return s2.getAge() - s1.getAge();
        }
    });
    

这段代码很冗长,有很多模板化的代码 (new Comparator..., @Override...)

  1. Lambda 表达式方式 Lambda 表达式让我们只关注最重要的部分:比较逻辑。
// 使用 Lambda 表达式
students.sort((Student s1, Student s2) -> {
return s2.getAge() - s1.getAge();
});

这已经简洁很多了,同时 Java 编译器足够聪明,可以进一步简化:

  • 可以省略参数类型 (Student s1, Student s2) -> (s1, s2)
  • 如果方法体只有一行,可以省略大括号 {} 和 return
// 终极简化版 Lambda
students.sort((s1, s2) -> s2.getAge() - s1.getAge());

list.sort() 是 Java 8 中 List 接口新增的默认方法,它接受一个 Comparator

3. Comparator 的静态辅助方法:更优雅的 Lambda

Java 8 的 Comparator 接口还提供了一系列静态辅助方法,让代码更具可读性和表现力,这些方法会返回一个 Comparator 实例。 最常用的就是 Comparator.comparing(),这里详细介绍一下这个方法:

  1. 单字段排序 Comparator.comparing() 接收一个函数,该函数从对象中提取用于排序的 Key
// 按年龄升序排序:
// 传入一个提取 age 的 Lambda
students.sort(Comparator.comparing(student -> student.getAge()));

// 使用“方法引用”(Method Reference) 的方式,更加简洁!
students.sort(Comparator.comparing(Student::getAge));

//Student::getAge 的写法叫做方法引用,它等同于 student -> student.getAge()。当 Lambda 表达式只是在调用一个已存在的方法时,方法引用是首选,因为它最清晰。

//按姓名升序排序:
students.sort(Comparator.comparing(Student::getName));
  1. 降序排序 如果想要降序排序,只需要在 Comparator 后面链式调用 .reversed() 方法即可。
//按年龄降序排序:
students.sort(Comparator.comparing(Student::getAge).reversed());

这种方式比 (s1, s2) -> s2.getAge() - s1.getAge() 更安全(因为避免了整数溢出问题),且更具可读性。

  1. 多字段组合排序 (链式比较) 这是 Comparator 辅助方法最强大的功能之一。如果需要先按一个字段排序,如果该字段值相同,再按另一个字段排序,可以使用 .thenComparing()
//需求:先按年龄升序排序,如果年龄相同,再按姓名升序排序。
students.sort(
Comparator.comparing(Student::getAge)
         .thenComparing(Student::getName)
);

这使得代码读起来就像在描述需求一样,非常直观

//更复杂的需求:先按年龄降序排序,如果年龄相同,再按姓名升序排序。
students.sort(
  Comparator.comparing(Student::getAge).reversed() // 年龄降序
            .thenComparing(Student::getName)      // 姓名升序
);
  1. 处理 null 值 如果排序的字段可能是 null,直接比较会抛出 NullPointerExceptionComparator 提供了优雅的处理方式。
  • Comparator.nullsFirst(): 将 null 值排在最前面。
  • Comparator.nullsLast(): 将 null 值排在最后面。
//需求:按姓名排序,但 null 姓名的学生排在最后。
// naturalOrder() 表示使用字段本身的自然顺序(这里是 String 的字典序)
students.sort(
Comparator.comparing(Student::getName, 
                    Comparator.nullsLast(Comparator.naturalOrder()))
);

4. 总结

场景 Lambda 推荐写法
单字段升序 list.sort(Comparator.comparing(ClassName::getFieldName));
单字段降序 list.sort(Comparator.comparing(ClassName::getFieldName).reversed());
多字段组合排序 list.sort(Comparator.comparing(C::getField1).thenComparing(C::getField2));
自定义复杂逻辑 list.sort((o1, o2) -> { /* 比较逻辑 */ });
处理 null 值 Comparator.comparing(C::getField, Comparator.nullsLast(Comparator.naturalOrder()))
阅读量: 21

评论区

登录后发表评论

正在加载评论...
相关阅读

Comparator 与 Comparable 的比较

# Comparator 与 Comparable 的比较 它们都是用来给对象排序的,但实现方式和使用场景有所不同: - `Comparable`:**内部比较器**,定义了类的**自然排序...

3
0