前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java8 集合中的 Lambda 表达式

Java8 集合中的 Lambda 表达式

作者头像
四火
发布2022-07-18 13:55:48
3350
发布2022-07-18 13:55:48
举报
文章被收录于专栏:四火的唠叨

本文翻译自《Java 8 Explained: Applying Lambdas to Java Collections》。

Lambdas 表达式是 Java 8 的主题,在 Java 平台上我们期待了很久。但是,如果如果我们不在集合中使用它的话,就损失了很大价值。把现有接口迁移成为 lambda 风格接口的问题已经通过 default methods,也就是 defender methods 解决了。在这篇文章里面我们来看一看 Java 集合里面的批量数据操作(bulk operation)。

批量操作

最初的变更文档已经说了,批量操作是 “给 Java 集合框架添加的用以批量操作数据的功能,而它是基于 lambda 函数实现的”。引用的话也就是在说,lambda 移植到 Java 8 对我来说的实际目的,就是它提供了一种新的使用集合的方式,这也是最重要的特性,表达式操作符可以并行执行,并且 lambda 是一个比常规表达式操作符更好的工具。

内部和外部的迭代

历史上,Java 集合是不能够表达内部迭代的,而只提供了一种外部迭代的方式,也就是 for 或者 while 循环。要描述内部迭代,我们需要用到 LambdaJ 这样的类库:

代码语言:javascript
复制
List persons = asList(new Person("Joe"), new Person("Jim"), new Person("John"));
forEach(persons).setLastName("Doe");

从上面的例子可以看出,我们不需要关心 last name 是怎么被设置到每一个 person 对象里面去的,也许这样的行为是支持并发执行的。现在我们可以在 Java 8 中使用类似的表达了:

代码语言:javascript
复制
persons.forEach(p -> p.setLastName("Doe"))

内部迭代其实和集合的批量操作并没有密切的联系,这只是一个小小的特性,借助它我们感受到语法表达上的变化。真正有意思的和批量操作相关的是新的流(stream)API。

流 API

新的 java.util.stream 包已经添加进 JDK 了,现在我们可以借助 Java 8 执行 filter/map/reduce 风格的操作了。

流 API 允许我们声明对数据进行串行或者并行的操作:

代码语言:javascript
复制
List persons = …   // sequential version
Stream stream = persons.stream();   //parallel version
Stream parallelStream = persons.parallelStream();

java.util.stream.Stream 接口提供了批量数据操作的入口,取得了对流实例的引用,我们就可以对集合执行如下有趣的任务了:

Filter

在数据流中实现过滤功能是首先我们可以想到的最自然的操作了。Stream 接口暴露了一个 filter 方法,它可以接受表示操作的 Predicate 实现来使用定义了过滤条件的 lambda 表达式。

代码语言:javascript
复制
List persons = …
Stream personsOver18 = persons.stream().filter(p -> p.getAge() > 18);

Map

假使我们现在过滤了一些数据,比如转换对象的时候。Map 操作允许我们执行一个 Function 的实现(Function<T,R> 的泛型 T,R 分别表示执行输入和执行结果),它接受入参并返回。首先,让我们来看看怎样以匿名内部类的方式来描述它:

代码语言:javascript
复制
Stream students = persons.stream()
      .filter(p -> p.getAge() > 18)
      .map(new Function() {
                  @Override
                  public Student apply(Person person) {
                     return new Student(person);
                  }
              });

现在,把上述例子转换成使用 lambda 表达式的写法:

代码语言:javascript
复制
Stream map = persons.stream()
        .filter(p -> p.getAge() > 18)
        .map(person -> new Student(person));

Lambda 在把参数传给 map 方法的时候,实际却并没有使用这个参数,那么我们就可以写成这样:

代码语言:javascript
复制
Stream map = persons.stream()
        .filter(p -> p.getAge() > 18)
        .map(Student::new);

Collect

“流” 抽象天生就该是持续的,我们使用流来描述操作,但是如果我们要获取最终结果的话,必须收集流产生的最终结果。Stream API 提供了一系列 “最终” 的方法,collect()(http://javadocs.techempower.com/jdk18/api/java/util/stream/Stream.html#collect(java.util.stream.Collector%29)方法就是其中的一个,我们借此可以收集操作的最终结果:

代码语言:javascript
复制
List students = persons.stream()
        .filter(p -> p.getAge() > 18)
        .map(Student::new)
        .collect(new Collector>() { … });

幸运的是,大多数情况下你不需要自己实现 Collector 接口,而是利用 Collectors 工具类:

代码语言:javascript
复制
List students = persons.stream()
        .filter(p -> p.getAge() > 18)
        .map(Student::new)
        .collect(Collectors.toList());

或者,如果我们想使用特定的实现类来收集结果:

代码语言:javascript
复制
List students = persons.stream()
        .filter(p -> p.getAge() > 18)
        .map(Student::new)
        .collect(Collectors.toCollection(ArrayList::new));

并行和串行

一个使用新的 Stream API 有趣的特性是它从来都不需要所谓串行或者并行的方法,可以从一开始就并行地消费数据,或者在处理流中的任意时刻转为串行的。

代码语言:javascript
复制
List students = persons.stream()
        .parallel()
        .filter(p -> p.getAge() > 18)  // filtering will be performed concurrently
        .sequential()
        .map(Student::new)
        .collect(Collectors.toCollection(ArrayList::new));

这里有隐藏的一点是,数据处理的并行部分会自动地自我管理,不需要我们自己来处理并发的问题。

总结

好了,要结束了。新的 Stream API 和 lambda 表达式给 Java 8 带来了很多新的特性。当然,在这篇文章以外还有很多没有谈及到,但愿很快我可以给你带给你更多有趣的特性。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档