Java 16 中新增的 Stream 接口的一些思考

Posted 干货满满张哈希

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 16 中新增的 Stream 接口的一些思考相关的知识,希望对你有一定的参考价值。

这里先提一个题外话,如果想看 JDK 不同版本之间有何差异,增加或者删除了哪些 API,可以通过下面这个链接查看: - https://javaalmanac.io/jdk/17/apidiff/11/ 路径中的两个版本就是要对比的两个版本,其界面如下:

Java

同时,我们也可以通过 JDK 内置 jdeps 工具查找过期以及废弃API以及对应的替换

jdeps --jdk-internals -R --class-path libs/* $project

libs是你的所有依赖的目录,$project是你的项目jar包,示例输出:

...
JDK Internal API Suggested Replacement
---------------- ---------------------
sun.misc.BASE64Encoder Use java.util.Base64 @since 1.8
sun.reflect.Reflection Use java.lang.StackWalker @since 9

Java 16 中针对 Stream API 有两个更新:

Java

增加了 ​​mapMulti​​ 和 ​​toList​​ 这两个 API。

mapMulti

​mapMulti​​ 其实主要是对现有的 ​​flatMap​​ 在某些场景使用起来不够合适的补充。​​flatMap​​ 是将一个对象映射为多个对象之后继续 Stream,例如将 ​​List>​​ 里面的每一个数字取出,转换成一个新的 ​​List​​:

integerLists.stream().flatMap(integers -> integers.stream()).collect(Collectors.toList());

这对于每个元素本身就是集合类型的场景来说,非常适用。我们再来看一个例子,假设有邮件这个 Record 类,包含 id,以及发送到的邮箱和抄送到的邮箱:

record Mail(int id, Set<String> sendTo, Set<String> cc) 

我们想找到一批邮件的所有不同的联系人,最后放到一个 List 中,可能会这么写:

Set<String> collect = mails.stream().flatMap(mail -> 
Set<String> result = new HashSet<>();
result.addAll(mail.sendTo());
result.addAll(mail.cc());
return result.stream();
).collect(Collectors.toSet());

但是,这样写显然很不优雅,首先是对于每一个 Mail 都创建了额外的 Set 和对应的 Stream,并且,对于每个 mail 的 sendTo 还有 cc 都遍历了两遍(addAll 一遍,后续 Stream 又一遍)。其实我们的目前只是将 mail 中的 cc 以及 sendTo 取出来,用于参与后续的 Stream。在这种场景下,就非常适合用 mapMulti:

Set<String> collect = mails.stream().<String>mapMulti((mail, consumer) -> 
mail.cc().forEach(consumer::accept);
mail.sendTo().forEach(consumer::accept);
).collect(Collectors.toSet());

可以看出: - mapMulti 的入参是一个 ​​BiConsumer​​,其实就是使用其参数中的 consumer 接收参与 Stream 后续的对象

- mapMulti 的思路就是将参数中的需要参与后续 Stream 的对象传入 consumer 来继续 Stream

- consumer 没有限制对象类型,想要限制必须加上形参 否则最后返回的是 ​​Set​​ 而不是 ​​Set​​ 假设 mail 的 sendTo 还有 cc 都需要去其他地方获取,使用 mapMulti 还可以实现:

Set<String> collect = mailIds.stream().<String>mapMulti((mailId, consumer) -> 
mailService.getCCById(mailId).forEach(consumer::accept);
mailService.getSendToById(mailId).forEach(consumer::accept);
).collect(Collectors.toSet());

还有一些比较有意思的用法,例如混合类型的 List 转换成统一类型:

class C 
static void expandIterable(Object e, Consumer<Object> c)
if (e instanceof Iterable<?> elements)
for (Object ie : elements)
expandIterable(ie, c);

else if (e != null)
c.accept(e);



public static void main(String[] args)
var nestedList = List.of(1, List.of(2, List.of(3, 4)), 5);
Stream<Object> expandedStream = nestedList.stream().mapMulti(C::expandIterable);

活用 ​​Optional.ifPresent(Consumer action)​​ 方法:

Stream.of(Optional.of("0"), Optional.of("1"), Optional.empty())
.mapMulti(Optional::ifPresent)
.forEach(System.out::print);

toList

对于 Stream 增加了 toList 直接转换成 List,由于不涉及 collect 里面的截断操作,所以比 collect 占用的内存更小,需要的操作更少并且更快。之前转换成 List,需要 ​


​collect(Collectors.toList())​​,生成的 List 是 ArrayList​,是可变的

但是这次新加的 Api,toList 生成的是 UnmodifiableList​,是不可变的。所以这两个 API


不能直接互相替换,需要做一些检查确认没有更改才能替换。

微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer

Java

以上是关于Java 16 中新增的 Stream 接口的一些思考的主要内容,如果未能解决你的问题,请参考以下文章

java ,返回流,IOException: Stream Closed,TryWithResource

浅谈JAVA11中新增的加密算法

java_JDK8中新增的时间API

.NET 8 Preview 1 中新增的 Random 方法

Java8 Stream 遍历数据效率差?实测结果出乎意料~~

Java8 Stream 遍历数据效率差?实测结果出乎意料~~