Java 8 Friday Goodies:Lambda和排序

Data Geekery ,我们喜欢Java。 而且,由于我们真的很喜欢jOOQ的流畅的API和查询DSL ,我们对Java 8将为我们的生态系统带来什么感到非常兴奋。 我们已经写了一些关于Java 8好东西的博客 ,现在我们觉得是时候开始一个新的博客系列了……

Java 8星期五

每个星期五,我们都会向您展示一些不错的教程风格的Java 8新功能,这些功能利用了lambda表达式,扩展方法和其他好东西。 您可以在GitHub上找到源代码

Java 8 Goodie:Lambda和排序

排序数组和集合是Java 8 lambda表达式的绝佳用例,原因很简单,因为Comparator自从JDK 1.2引入以来一直一直是@FunctionalInterface 。 现在,我们可以将lambda表达式形式的Comparators提供给各种sort()方法。

对于以下示例,我们将使用此简单的Person类:

static class Person {
    final String firstName;
    final String lastName;

    Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return "Person{" +
                "firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                '}';
    }
}

显然,我们也可以通过使它实现Comparable来向Person添加自然排序,但让我们关注外部Comparators 。 考虑以下Person列表,其名称是使用一些在线随机名称生成器生成的:

List<Person> people =
Arrays.asList(
    new Person("Jane", "Henderson"),
    new Person("Michael", "White"),
    new Person("Henry", "Brighton"),
    new Person("Hannah", "Plowman"),
    new Person("William", "Henderson")
);

我们可能想按姓氏然后按名字对它们进行排序。

用Java 7排序

这样的Comparator器的一个“经典” Java 7示例是这样的:

people.sort(new Comparator<Person>() {
  @Override
  public int compare(Person o1, Person o2) {
    int result = o1.lastName.compareTo(o2.lastName);

    if (result == 0)
      result = o1.firstName.compareTo(o2.firstName);

    return result;
  }
});
people.forEach(System.out::println);

以上将产生:

Person{firstName='Henry', lastName='Brighton'}
Person{firstName='Jane', lastName='Henderson'}
Person{firstName='William', lastName='Henderson'}
Person{firstName='Hannah', lastName='Plowman'}
Person{firstName='Michael', lastName='White'}

用Java 8排序

现在,让我们将以上内容翻译为等效的Java 8代码:

Comparator<Person> c = (p, o) ->
    p.lastName.compareTo(o.lastName);

c = c.thenComparing((p, o) ->
    p.firstName.compareTo(o.firstName));

people.sort(c);
people.forEach(System.out::println);

结果显然是相同的。 如何阅读以上内容? 首先,我们将lambda表达式分配给本地Person Comparator变量:

Comparator<Person> c = (p, o) ->
    p.lastName.compareTo(o.lastName);

Scala,C#或Ceylon不同,后者通过val关键字(或类似关键字) 知道从表达式到局部变量声明的类型推断,而Java从变量(或参数,成员)声明到要分配的表达式执行类型推断。

换句话说,类型推断是从“左到右”而不是从“右到左”执行的。 这使得链接Comparators有点麻烦,因为Java编译器在将比较器传递给sort()方法之前不能延迟lambda表达式的类型推断

但是,一旦为变量分配了Comparator ,就可以通过thenComparing()流畅地链接其他比较器:

c = c.thenComparing((p, o) ->
    p.firstName.compareTo(o.firstName));

最后,我们将其传递给List的新sort()方法,这是直接在List接口上实现的默认方法:

default void sort(Comparator<? super E> c) {
    Collections.sort(this, c);
}

上述限制的解决方法

虽然Java的类型推断“局限性”可能会让人感到沮丧,但我们可以通过创建通用的IdentityComparator解决类型推断:

class Utils {
    static <E> Comparator<E> compare() {
        return (e1, e2) -> 0;
    }
}

使用上面的compare()方法,我们可以编写以下流畅的比较器链:

people.sort(
    Utils.<Person>compare()
         .thenComparing((p, o) -> 
              p.lastName.compareTo(o.lastName))
         .thenComparing((p, o) -> 
              p.firstName.compareTo(o.firstName))
);

people.forEach(System.out::println);

提取密钥

这可以变得更好。 由于我们通常会比较两个Comparator参数的相同POJO / DTO值,因此我们可以通过“键提取器”功能将它们提供给新的API。 它是这样工作的:

people.sort(Utils.<Person>compare()
      .thenComparing(p -> p.lastName)
      .thenComparing(p -> p.firstName));
people.forEach(System.out::println);

因此,在给定Person p我们为API提供了提取例如p.lastName 。 实际上,一旦我们使用了键提取器,我们就可以省略我们自己的实用程序方法,因为这些库还有一个comparing()方法来初始化整个链:

people.sort(
    Comparator.comparing((Person p) -> p.lastName)
          .thenComparing(p -> p.firstName));
people.forEach(System.out::println);

同样,我们需要帮助编译器,因为它不能推断所有类型,即使原则上在这种情况下sort()方法将提供足够的信息。 要了解有关Java 8的通用类型推断的更多信息,请参见我们以前的博客文章

结论

与Java 5一样,可以在JDK库中看到升级的最大改进。 当Java 5为Comparators带来类型安全性时,Java 8使它们易于读取和编写(给出或接受奇数类型推理怪癖)。

Java 8将彻底改变我们编程的方式,下周,我们将看到Java 8如何影响我们与SQL交互的方式。

参考: Java 8 Friday Goodies:Lambda和Sorting,来自我们的JCG合作伙伴 Lukas Eder在JAVA,SQL和JOOQ博客上。

翻译自: https://www.javacodegeeks.com/2014/02/java-8-friday-goodies-lambdas-and-sorting.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值