这里先提一个题外话,如果想看 JDK 不同版本之间有何差异,增加或者删除了哪些 API,可以通过下面这个链接查看:

路径中的两个版本就是要对比的两个版本,其界面如下:

同时,我们也可以通过 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 有两个更新:

增加了 mapMultitoList 这两个 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<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 没有限制对象类型,想要限制必须加上形参 <String> 否则最后返回的是 Set<Object> 而不是 Set<String>

假设 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<? super T> 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

最新文章

  1. Tomcat7配置及其servlet调用详解
  2. 使用CallableStatement的用法
  3. 22Mybatis_订单商品数据模型_多对多查询以及对多对多查询的总结
  4. js正则表达式的验证示例
  5. Android WebView缓存分析
  6. 批量更新数据小心SQL触发器的陷阱
  7. px,dp,dip,sp,in,mm,pt详细分析
  8. .net mvc Authorization Filter,Exception Filter与Action Filter
  9. 将vs屏幕上内容重定向到一个log文本中
  10. iOS开发中在UIWebView中添加Gif动态图
  11. android双待手机获取每一张SIM卡的imei
  12. echarts的地图点击事件
  13. vue引入外部.css文件,webpack将其与.vue中的样式混合打包了,怎么办?
  14. VS2012及VS2013连接SQL2008提示 Could not load file or assembly &#39;Microsoft.SqlServer.Management.Sdk.Sfc&#39;
  15. MongoDB &quot;$&quot; 字符 下标位置
  16. BitMap位图与海量数据的理解与应用
  17. live-server
  18. 【Ansible 文档】【译文】Windows 支持
  19. CSS——【元素内边距padding、元素外边距margin、元素边框border-width、元素大小width/height】与【元素显示大小】的关系
  20. PHP安全相关的配置(1)

热门文章

  1. android studio 使用 aidl(三)权限验证
  2. ListView的item不能点击(焦点冲突问题)
  3. mysql_取分组后的前几行值
  4. spring注解-web
  5. 关系型数据库和非关系型数据库区别、oracle与mysql的区别
  6. SpringBoot服务间使用自签名证书实现https双向认证
  7. shell脚本 Linux系统安全监测
  8. 【划重点】Python遍历列表的四种方法
  9. proxy跨域
  10. [BUUCTF]REVERSE——[ACTF新生赛2020]rome