在上一篇文章中,compare和comparator我们已经了解了 Comparable 和 Comparable 接口以及它们对应的 lambda 表达式。
我们还了解了 Comparator 接口中的两个方法:comparing() 和 thenComparing()。这两种方法允许我们以更具声明性的方式进行编程。
首先,让我们看下下面的 Student 类。
class Student {
private final int id;
private final String name;
private final String course;
private final int deptNo;
Student(int id, String name, String course, int deptNo) {
this.id = id;
this.name = name;
this.deptNo = deptNo;
this.course = course;
}
public int getId() { return id; }
public String getName() { return name; }
public int getDeptNo() { return deptNo; }
public String toString() {
return String.format("%d,%s,%s,%d", id, name, course, deptNo);
}
}
假设我们已将上面的内容转换为 List<Student> 对象。
现在我们要对这个列表进行排序,首先对具有相同 DEPTNO 的所有学生进行排序,然后如果两个学生具有相同的DEPTNO,则必须按 ID 升序对他们进行排序。
我们可以简单地将 lambda 写成如下所示。
students.sort((s1, s2) -> {
int diff = s1.getDeptNo() - s2.getDeptNo();
return diff == 0 ? s1.getId() - s2.getId() : diff;
});
但是如果我们希望它们按降序排列怎么办?
我们必须使用 lambda 编写另一个排序逻辑,如下所示。
students.sort((s1, s2) -> {
int diff = s2.getDeptNo() - s1.getDeptNo();
return diff == 0 ? s2.getId() - s1.getId() : diff;
});
问题是如果我们想要另一种排序方式,我们必须编写另一个 lambda。
有一种更好、更具声明性的方法,就是可以使用 Comparator.comaparing() 和 Comparator.thenComparing() 来完成此操作。
Comparator<Student> deptWiseStdIdComparator =
Comparator.comparing(Student::getDeptNo)
.thenComparing(Student::getId)
students.sort(deptWiseStdIdComparator);
我故意把它分成两行更容易理解。
Comparator.comparing(Student::getDeptNo):这将返回一个比较部门编号的Comparator。
Comparator.thenComparing(Student::getId):这会返回一个 Comparator,它是与此关联的Comparator和作为参数提供给它的函数表达式的组合。
如果按降序排列呢?这样做的好处是我们不必编写另一个逻辑。我们只需要将其反转,如下所示
Comparator<Student> revDeptWiseStdIdComparator =
deptWiseStdIdComparator.reversed();
returned() 方法翻转整个排序。
但我们可以说我们不想要这样。我们想要的是部门应按升序显示,但学生 ID 应按降序显示。请记住,reverse() 方法会将整个排序颠倒过来。
为了实现这一点,我们应该通过如下拆分语句来实现这一点。
students.sort(Comparator
.comparing(Student::getDeptNo)
.reversed()
.thenComparing(Student::getId));
students.forEach(System.out::println);
首先,我们使用反向的部门编号Compartor,然后使用 ID 比较器,并使用 thenComparing() 将它们连接起来。
如果我们想要相反的情况,即按部门升序但按学生降序,我们该怎么写呢?。你可能会想到这样写?
students.sort(Comparator
.comparing(Student::getDeptNo)
.thenComparing(Student::getId)
.reversed());
students.forEach(System.out::println);
但这并没有给我们带来我们想要的结果。最后使用reverse()会将整个排序颠倒过来。
非常重要
如果降序比较位于最后,那么我们应该谨慎使用reverse()方法,因为它会将整个排序颠倒过来。
如果您想在链的末尾使用reverse(),那么可以将整个排序视为相反的方式。
我们必须编写与我们想要的相反的整个排序,然后使用reverse()来否定整个排序。
students.sort(Comparator
.comparing(Student::getDeptNo)
.reversed()
.thenComparing(Student::getId)
.reversed());
students.forEach(System.out::println);
我们只需要反转它,所以我们使用了reverse()。
或者您可以拆分Comparator并按如下所述进行操作。
Comparator<Student> deptWiseComparator =
Comparator.comparing(Student::getDeptNo);
Comparator<Student> stdIdRevComparator =
Comparator.comparing(Student::getId).reversed();
students.sort(deptWiseComparator.thenComparing(stdIdRevComparator));
students.forEach(System.out::println);
这就是本文的全部内容。至此,我们这个系列就可以结束了。
总结
- comparing和thenComparing方法提供了一种更具声明性的编写比较代码的方式。
- 如果我们想反转特定的比较,那么我们可以简单地调用reversed()。
- 在我们想要的任何比较中,如果reversed()在末尾,那么我们必须考虑与我们想要的完全相反的比较,来获得一致的结果。