3.5 多次调用流操作

用户也可以选择每一步强制对函数求值,而不是将所有的方法调用链接在一起,但是,最好不要如此操作。例3-24展示了如何用如上述不建议的编码风格来找出专辑上所有演出乐队的国籍,例3-25则是之前的代码,放在一起方便比较。

例3-24 误用Stream的例子

  1. List<Artist> musicians = album.getMusicians()
  2. .collect(toList());
  3. List<Artist> bands = musicians.stream()
  4. .filter(artist -> artist.getName().startsWith("The"))
  5. .collect(toList());
  6. Set<String> origins = bands.stream()
  7. .map(artist -> artist.getNationality())
  8. .collect(toSet());

例3-25 符合Stream使用习惯的链式调用

  1. Set<String> origins = album.getMusicians()
  2. .filter(artist -> artist.getName().startsWith("The"))
  3. .map(artist -> artist.getNationality())
  4. .collect(toSet());

例3-24所示代码和流的链式调用相比有如下缺点:

  • 代码可读性差,样板代码太多,隐藏了真正的业务逻辑;
  • 效率差,每一步都要对流及早求值,生成新的集合;
  • 代码充斥一堆垃圾变量,它们只用来保存中间结果,除此之外毫无用处;
  • 难于自动并行化处理。

当然,刚开始写基于流的程序时,这样的情况在所难免。但是如果发现自己经常写出这样的代码,就要反思能否将代码重构得更加简洁易读。

3.5 多次调用流操作 - 图1如果此时还不习惯Stream API中大量的链式操作,也很正常。随着练习时间增加,经验也会越来越丰富,这些概念理解起来也更加自然。因此,尚未习惯不能成为拆开链式操作、写出形如例3-24中代码的理由。像使用建造者模式那样,按规则写出每一行代码,可以帮助用户慢慢习惯这种链式操作。