Java 16 中针对 Stream API 有两个更新:
增加了 mapMulti
和 toList
这两个 API。
mapMulti
mapMulti
其实主要是对现有的 flatMap
在某些场景使用起来不够合适的补充。flatMap
是将一个对象映射为多个对象之后继续 Stream,例如将 List<List<Integer>>
里面的每一个数字取出,转换成一个新的 List<Integer>
:
integerLists.stream().flatMap(integers -> integers.stream()).collect(Collectors.toList());
复制代码
这对于每个元素本身就是集合类型的场景来说,非常适用。我们再来看一个例子,假设有邮件这个 Record 类,包含 id,以及发送到的邮箱和抄送到的邮箱:
record Mail(int id, Set sendTo, Set cc) {}
复制代码
我们想找到一批邮件的所有不同的联系人,最后放到一个 List 中,可能会这么写:
Set collect = mails.stream().flatMap(mail -> {
Set 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 collect = mails.stream().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 没有限制对象类型,想要限制必须加上形参
<String>
否则最后返回的是Set<Object>
而不是Set<String>
假设 mail 的 sendTo 还有 cc 都需要去其他地方获取,使用 mapMulti 还可以实现:
Set collect = mailIds.stream().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 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) {
读者福利
更多笔记分享
ublic static void main(String[] args) {
读者福利
[外链图片转存中…(img-qAoapQwp-1714303188729)]
更多笔记分享
[外链图片转存中…(img-10cdFq2i-1714303188729)]