使用Lambda快速构建Comparator

本文转载与本人个人博客

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

猜你喜欢

转载自blog.csdn.net/Tales_/article/details/82432582