本文转载与本人个人博客
1.概述
在算法笔试过程中,我们经常会遇到一些排序(有时候并不会要求我们编写排序本身的代码,而是要求我们对数据做一些简单的处理),Collections工具类和List提供了排序算法,大多时候要求我们为排序方法传递一个Comparator,我们经常使用匿名类来实现这个Comparator,但是仍显繁冗。本文就此目的总结了一些使用Lambda实现Comparator的方法。
在本文,我们将首先介绍Java 8中的Lambda支持,特别是如何利用它来编写Comparator并对Collection进行排序。
首先,让我们定义一个简单的实体类:
public class Human {
private String name;
private int age;
public Human() {
super();
}
public Human(String name, int age) {
super();
this.name = name;
this.age = age;
}
// standard getters/setters, equals and hashcode
}
2.没有Lambdas的基本排序
在Java 8之前,对集合进行排序一般要为sort使用的Comparator创建匿名内部类:
new Comparator<Human>() {
@Override
public int compare(Human h1, Human h2) {
return h1.getName().compareTo(h2.getName());
}
}
这只会被用来排序Person实体的name:
3.有Lambda支持的基本排序
随着Lambdas的引入,我们现在可以绕过匿名内部类,并通过简单,功能性的语义实现相同的结果:
(final Human h1, final Human h2) -> h1.getName().compareTo(h2.getName());
在这里我们还使用了在Java 8中添加到java.util.List的新的sort API,而不是旧的Collections.sort API
4.没有类型定义的基本排序
我们可以通过不指定类型定义来进一步简化表达式——编译器能够自己推断这些:
(h1, h2) -> h1.getName().compareTo(h2.getName())
5.使用引用静态方法排序
接下来,我们将使用Lambda Expression执行排序,并引用静态方法。
首先,我们将定义方法compareByNameThenAge - 与Comparator 对象中的compare方法完全相同的签名:
public static int compareByNameThenAge(Human lhs, Human rhs) {
if (lhs.name.equals(rhs.name)) {
return lhs.age - rhs.age;
} else {
return lhs.name.compareTo(rhs.name);
}
}
现在,我们将使用此引用调用humans.sort方法:
humans.sort(Human::compareByNameThenAge);
最终结果是使用静态方法作为比较器对集合进行排序:
@Test
public void
givenMethodDefinition_whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {
List<Human> humans = Lists.newArrayList(
new Human("Sarah", 10),
new Human("Jack", 12)
);
humans.sort(Human::compareByNameThenAge);
Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}
6.使用提取的Comparator进行排序
我们还可以通过使用实例方法引用和Comparator.comparing方法来避免定义比较逻辑本身- 该方法基于该函数提取和创建Comparable。
我们将使用getter getName()来构建Lambda表达式并按名称对列表进行排序:
@Test
public void
givenInstanceMethod_whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {
List<Human> humans = Lists.newArrayList(
new Human("Sarah", 10),
new Human("Jack", 12)
);
Collections.sort(
humans, Comparator.comparing(Human::getName));
assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}
7.反向排序
JDK 8还引入了一个用于反转比Comparator的辅助方法- 我们可以快速使用它来反转我们的排序:
@Test
public void whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
List<Human> humans = Lists.newArrayList(
new Human("Sarah", 10),
new Human("Jack", 12)
);
Comparator<Human> comparator
= (h1, h2) -> h1.getName().compareTo(h2.getName());
humans.sort(comparator.reversed());
Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}
8.按多个条件排序
比较lambda表达式不一定非常简单,我们也可以编写更复杂的表达式。例如,首先按名称排序实体,然后按年龄排序:
@Test
public void whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {
List<Human> humans = Lists.newArrayList(
new Human("Sarah", 12),
new Human("Sarah", 10),
new Human("Zack", 12)
);
humans.sort((lhs, rhs) -> {
if (lhs.getName().equals(rhs.getName())) {
return lhs.getAge() - rhs.getAge();
} else {
return lhs.getName().compareTo(rhs.getName());
}
});
Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}
9.按多个条件排序——组合
相同的比较逻辑,首先按名称排序,然后按年龄排序 也可以通过Comparator的新组合支持来实现。
从JDK 8开始,我们现在可以将多个比较器链接在一起,以构建更复杂的比较逻辑:
@Test
public void
givenComposition_whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {
List<Human> humans = Lists.newArrayList(
new Human("Sarah", 12),
new Human("Sarah", 10),
new Human("Zack", 12)
);
humans.sort(
Comparator.comparing(Human::getName).thenComparing(Human::getAge)
);
Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}