Java 8 – 使用Lambdas表达式进行强力比较

使用Lambdas表达式进行强力比较

翻译来源: Eugen Paraschiv 的 Java 8 – Powerful Comparison with Lambdas

1.概述

在本教程中,我们将首先了解Java 8中的Lambda支持 - 特别是如何利用它来编写Comparator并对Collection进行排序。

本文是Baeldung上“Java - Back to Basic”系列的一部分。

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

public class Human {
    private String name;
    private int age;
 
    // standard constructors, getters/setters, equals and hashcode
}

2.没有lambda的基本排序

在Java 8之前,对集合进行排序将涉及为排序中使用的Comparator创建匿名内部类:

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

这只是用于对人类实体列表进行排序:

@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支持的基本排序

随着Lambdas的引入,我们现在可以绕过匿名内部类,并通过简单,功能的语义实现相同的结果:

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

请注意,我们还使用了添加到Java 8中的java.util.List的新排序API - 而不是旧的Collections.sort API。

4.没有类型定义的基本排序

我们可以通过不指定类型定义来进一步简化表达式 - 编译器能够自己推断这些:

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

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.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还引入了一个用于反转比较器的辅助方法 - 我们可以快速使用它来反转我们的排序:

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

10.使用Stream.sorted()对列表进行排序

我们还可以使用Java 8的Stream sorted()API对集合进行排序。

我们可以使用自然排序以及比较器提供的排序对流进行排序。 为此,我们有sorted()API的两个重载变体:

  • sorted() - 使用自然排序对Stream的元素进行排序; 元素类必须实现Comparable接口。
  • sorted(Comparator <?super T> comparator) - 根据Comparator实例对元素进行排序

让我们看一个如何使用自然排序的sorted()方法的示例:

@Test
public final void givenStreamNaturalOrdering_whenSortingEntitiesByName_thenCorrectlySorted() {
    List<String> letters = Lists.newArrayList("B", "A", "C");
     
    List<String> sortedLetters = letters.stream().sorted().collect(Collectors.toList());
    assertThat(sortedLetters.get(0), equalTo("A"));
}

现在让我们看看我们如何使用自定义Comparator与sorted() API:

@Test
public final void givenStreamCustomOrdering_whenSortingEntitiesByName_thenCorrectlySorted() {   
    List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
    Comparator<Human> nameComparator = (h1, h2) -> h1.getName().compareTo(h2.getName());
     
    List<Human> sortedHumans = humans.stream().sorted(nameComparator).collect(Collectors.toList());
    assertThat(sortedHumans.get(0), equalTo(new Human("Jack", 12)));
}

如果我们使用Comparator.comparing()方法,我们可以进一步简化上面的例子:

@Test
public final void givenStreamComparatorOrdering_whenSortingEntitiesByName_thenCorrectlySorted() {
    List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
  
    List<Human> sortedHumans = humans.stream()
      .sorted(Comparator.comparing(Human::getName))
      .collect(Collectors.toList());
       
    assertThat(sortedHumans.get(0), equalTo(new Human("Jack", 12)));
}

11.使用Stream.sorted()反向排序列表

我们也可以使用Stream.sorted()来反向排序集合。

首先,让我们看一个如何将sorted()方法与Comparator.reverseOrder()组合以反向自然顺序对列表进行排序的示例:

@Test
public final void givenStreamNaturalOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
    List<String> letters = Lists.newArrayList("B", "A", "C");
 
    List<String> reverseSortedLetters = letters.stream()
      .sorted(Comparator.reverseOrder())
      .collect(Collectors.toList());
       
    assertThat(reverseSortedLetters.get(0), equalTo("C"));
}

现在,让我们看看如何使用sorted()方法和自定义Comparator:

@Test
public final void givenStreamCustomOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
    List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
    Comparator<Human> reverseNameComparator = (h1, h2) -> h2.getName().compareTo(h1.getName());
 
    List<Human> reverseSortedHumans = humans.stream().sorted(reverseNameComparator)
      .collect(Collectors.toList());
    assertThat(reverseSortedHumans.get(0), equalTo(new Human("Sarah", 10)));
}

请注意,compareTo的调用被翻转,这就是正在进行的反转。

最后,让我们使用Comparator.comparing()方法简化上面的示例:

@Test
public final void givenStreamComparatorOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
    List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
 
    List<Human> reverseSortedHumans = humans.stream()
      .sorted(Comparator.comparing(Human::getName, Comparator.reverseOrder()))
      .collect(Collectors.toList());
     
    assertThat(reverseSortedHumans.get(0), equalTo(new Human("Sarah", 10)));
}

12.总结

这篇文章说明了使用Java 8 Lambda表达式对List进行排序的各种令人兴奋的方法 - 正好超越了语法糖并转化为真正强大的功能语义。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值