乐趣区

关于java:Java-16-中新增的-Stream-接口的一些思考

这里先提一个题外话,如果想看 JDK 不同版本之间有何差别,减少或者删除了哪些 API,能够通过上面这个链接查看:

  • https://javaalmanac.io/jdk/17…

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

同时,咱们也能够通过 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

退出移动版