返回介绍

Java8:Lambda 表达式增强版 Comparator 和排序

发布于 2025-02-26 22:54:39 字数 6792 浏览 0 评论 0 收藏 0

1、概述

在这篇教程里,我们将要去了解下即将到来的 JDK 8 (译注,现在 JDK 8 已经发布了) 中的 Lambda 表达式——特别是怎样使用它来编写 Comparator 和对集合(Collection)进行排序。

这篇文章是 Baeldung 上的 “Java ——回归基础”(“Java – Back to Basic”)系列 的一部分。

首先,让我们先定义一个简单的实体类:

public class Human {
    private String name;
    private int age;

    public Human() {
        super();
    }

    public Human(final String name, final int age) {
        super();

        this.name = name;
        this.age = age;
    }

    // standard getters and setters
}

2、不使用 Lambda 表达式的基本排序

在 Java 8 之前,对集合进行排序要 为 Comparator 创建一个匿名内部类 用来排序:

new Comparator<Human>() {
@Override
public int compare(Human h1, Human h2) {
return h1.getName().compareTo(h2.getName());
}
}

简单地用它来对 Human 实体列表进行排序:

@Test
public void givenPreLambda_whenSortingEntitiesByName_thenCorrectlySorted() {
List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
Collections.sort(humans, new Comparator<Human>() {
@Override
public int compare(Human h1, Human h2) {
return h1.getName().compareTo(h2.getName());
}
});
Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}

3、使用 Lambda 表达式的基本排序

根据 Lambda 表达式的介绍,我们现在可以不使用匿名内部类,只使用 简单实用的语义 就可以得到相同的结果。

(final Human h1, final Human h2) -> h1.getName().compareTo(h2.getName());

类似地,我们现在可以像之前那样来测试它的行为:

@Test
public void whenSortingEntitiesByName_thenCorrectlySorted() {
List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));

humans.sort((Human h1, Human h2) -> h1.getName().compareTo(h2.getName()));
Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}

注意:我们同样使用 新的 sort API,这个 API 在 Java 8 里被添加到 java.util.List ——而不是旧的 Collections.sort API。

4、没有类型定义( Type Definitions)的基本排序

我们通过不指定类型定义来进一步简化表达式 —— 编译器自己可以进行类型判断

(h1, h2) -> h1.getName().compareTo(h2.getName())

测试仍然很相似:

@Test
public void givenLambdaShortForm_whenSortingEntitiesByName_thenCorrectlySorted() {
    List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));

    humans.sort((h1, h2) -> h1.getName().compareTo(h2.getName()));
    Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}

5、使用静态方法的引用来排序

下面我们将要使用带有静态方法引用的 Lambda 表达式去进行排序。

首先,我们要定义 compareByNameThenAge 方法 ——这个方法拥有与 Comparator 对象里的 compareTo 方法完全相同的签名:

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);

最终结果是一个使用静态方法作为 Comparator 的有效的排序集合:

@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。

我们准备使用 getName() getter 方法去建造 Lambda 表达式并通过 name 对列表进行排序:

@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));
    Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}

7、反转排序

JDK 8 同样提供了一个有用的方法用来 反转 Comparator(reverse 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 表达式不一定都是这么简单的——我们 同样可以编写更复杂的表达式, 比如先根据 name 后根据 age 来对实体进行排序:

@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、多条件组合排序

同样的比较逻辑——先根据 name 进行排序其次是 age,同样可以通过 Comparator 新的组合支持来实现。

从 JDK 8 开始,我们现在可以把多个 Comparator 链在一起(chain together) 去建造更复杂的比较逻辑:

@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)));
}

10、总结

这篇文章举例说明了多种令人兴奋的方法: 使用 Java 8 Lambda 表达式对列表进行排序 ——正确使用过去的语法糖和真正、强大实用的语义。

所有这些例子的实现和代码片段都可以在 我的 github 项目 上获取到——这是一个基于 Eclipse 的项目,所以它应该很容易被导入和运行。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文