Streams 非常强大,并且能够用简单几行代码实现你所期望的功能要点。它们正常运行时非常流畅优雅,但运行不符合预期时却让人苦不堪言。让我们学习一下如何用 IntelliJ 调试你的 Java Streams 代码,并洞察 Stream 的中间操作。


在这篇文章中,我将在例子里用到 Sakila 样本数据库和 Speedment Stream ORM 框架。


原理


让我们从一个简单的 Stream 开始,我们可以在 IntelliJ 为其创建一个基本的 Stream debugger:


List<String> strings = Stream.of("C", "A", "B")

    .sorted()

    .collect(toList());


上面的代码创建了一个由字符串 “A”、“B”、“C”组成的 Stream。紧接着对这个 Stream 进行 sorted() 操作,从而创建了一个新的 Stream(至少在 Java 8-10 中是这样),其中的元素是第一个 Stream 的元素按字母排序的结果。也就是说,第二个 Stream 包含“A”、“B”、“C”三个元素。最后,这些将元素放到一个 List 中。


上面的代码和下面的等价:


Stream<String> s0 = Stream.of("C", "B", "A"); // "C", "A", "B"

Stream<String> s1 = s0.sorted();              // "A", "B", "C"

List<String> strings = s1.collect(toList());  // [“A”, “B”, “C”]


这大体上演示了 Stream debugger 如何工作。它将一个 stream 管道操作分割成多个代码片段,一步一步地调用中间运算操作,从而可以保留每一步操作的元素内容以供分析。


Stream.of("C", "B", "A")

  .peek(saveStep(0))

  .sorted()

  .peek(saveStep(1))

  .collect(toList()); // The final result is saved to step 2


注意:真正的技术实现并不是上面这样,它只是提供了很好的概览。


在 IntelliJ’s debugger 中有更加形象化的表示:


图片


它简洁明了地展示了 Stream 管道里的每个中间操作细节,以及最后结果。


调用


想调用 stream debugger 的话,首先要在 Stream 定义处设置断点:


图片


然后,启动调试会话(以 debug 模式运行):


图片


当到达断点时,可以按指定的按钮(可能有些不好找)来调用 Stream debugger,下面用红圈标出:


图片


这样就打开了 stream debugger,和上文所展示的一样。


数据库 Streams


我将使用 Speedment(stream ORM),它允许通过标准的 Java Streams 操作来查询数据库,因此也能通过 IntelliJ 来调试操作。可以通过 Speedment initializer 来创建一个 Speedment 项目。


可以通过下面方式创建一个 Java 应用。


Speedment app = new SakilaApplicationBuilder()

    .withPassword("sakila-password") // Replace with your own password

    .build();

FilmManager films = app.getOrThrow(FilmManager.class);


现在我们可以对数据库“film”表进行 stream 操作。下面是个例子:


List<Film> map = films.stream()

    .filter(Film.LENGTH.equal(60))

    .sorted(Film.RATING.reversed())

    .collect(toList());


代码将从所有 Film(电影) 对象中筛选出长度为 60 分钟的,然后通过 Film.RATING(评级)对这些 Film 对象进行排序(降序),最后将所有元素放入一个 List。


我们调用 Stream debugger 时,会看到下面这张图:


图片


我们可以看到,初始 stream 中有 1000 部电影。筛选操作后,只剩 8 部电影,紧接着排序并放到一个 List。


计算统计


假设我们要计算所有 PG-13 级别(电影分级制度中的一种)电影的最小时长、最大时长以及平均时长。代码如下:


IntSummaryStatistics stat = films.stream()

    .filter(Film.RATING.equal("PG-13"))

    .mapToInt(Film.LENGTH.asInt())

    .summaryStatistics();


Stream debugger 展示如下:


图片


可以看出,我们可以与 Stream debugger 交互,并在 stream 管道中点击元素来高亮显示,也可以在元素间滚动查看单个操作步骤。


Speedment 优化了数据库 Stream 中间操作,并将其融合进 SQL 查询。但使用 Stream debugger 时,优化并没有生效,以便让我们可以看到 Stream 管道中的所有操作步骤。


结论


Stream debugger 是个隐藏的瑰宝,对于 Streams 工作有很大帮助。


我认为这是 IntelliJ 团队提供的非常棒的特性。


更多相关文章

  1. mall整合Mongodb实现文档操作
  2. 为什么推荐使用for-each而不是for循环遍历元素?
  3. 看完这篇Linux基本的操作就会了
  4. 操作系统面试题汇总(找工作必备)
  5. 还有这种操作?浅析为什么要看源码
  6. 优雅地操作 List、Map
  7. java8中的一个骚操作-方法引用(使代码看起来很高大上)

随机推荐

  1. Android中自定义Checkbox
  2. Android Developers:日历提供者
  3. Android下载网络图片到本地
  4. android如何改变默认横竖屏方向
  5. Android判断当前系统语言
  6. android SDK 更新出现错误的解决办法
  7. WebView 简单使用一
  8. layout_weight 权重
  9. Android各版本对应的SDK版本
  10. 使用Android studio导入源码工程