使用Stream.peek在Java Streams内部进行窥视

对于刚接触JDK 8的管道和流的Java开发人员而言, Stream接口提供的peek(Consumer)方法可以用作可视化流操作行为的有用工具。 即使是更熟悉Java流和聚合操作的Java开发人员,有时也会发现Stream.peek(Consumer)对于理解复杂的中间流操作的含义和交互作用很有用。

Stream.peek(Consumer)方法需要一个Consumer ,它实际上是一个接受单个参数却不返回任何内容的代码块。 peek(Consumer)方法返回传递给它的流的相同元素,因此除非传递给peek(Consumer)方法的代码块使流中的对象发生突变,否则流的内容将不会更改。 。 Stream.peek(Consumer)的绝大多数用途很可能是在调用该方法时以只读方式打印流中对象的内容。

Stream.peek(Consumer)的基于Javadoc的API文档更加详细地说明了此方法的行为,并提供了其用法示例。 该示例在下面的代码清单中略作修改:

final List<String> strings
   = Stream.of("one", "two", "three", "four")
      .peek(e-> out.println("Original Element: " + e))
      .filter(e -> e.length() > 3)
      .peek(e -> out.println("Filtered value: " + e))
      .map(String::toUpperCase)
      .peek(e -> out.println("Mapped value: " + e))
      .collect(Collectors.toList());
out.println("Final Results: " + strings);

执行上述代码后,其关联的输出如下所示:

Original Element: one
Original Element: two
Original Element: three
Filtered value: three
Mapped value: THREE
Original Element: four
Filtered value: four
Mapped value: FOUR
Final Results: [THREE, FOUR]

输出通过提供给它们的元素讲述了流操作的故事。 首次执行中间peek操作将使用前缀“原始元素:”将原始流中的每个元素写到系统输出中。 对于每个原始String不会执行稍后发生的中间peek操作的实例,因为这些peek操作中的每一个都至少在进行一次过滤之后发生。

启用peek输出还清楚地显示了在每个String元素上以等效的大写形式执行中间操作映射的结果。 收集操作是终止操作,因此此后不进行任何peekpeek操作的战略位置可提供对发生的流处理的重要见解。

用于Stream.peek(Consumer)Javadoc指出:“存在此方法主要是为了支持调试,您希望在其中看到元素流过管道中特定点的情况。” 这正是上面显示的示例和输出所演示的,并且可能是Stream.peek(Consumer)的最常见应用。

Stream.peek(Consumer)的Javadoc文档以以下描述性句子开头:“返回由该流的元素组成的流,并在从结果流中消耗掉元素时对每个元素执行附加的操作。” 在前面的示例中,在消耗每个元素时对每个元素执行的操作只是将其字符串表示形式写入标准输出。 但是,采取的动作可以是可以指定为Consumer任何东西(任何接受单个参数且不返回任何参数的代码块)。 下一个示例演示如何甚至可以使用peek(Consumer)更改流上对象的内容。

在本文的第一个示例中, peek(Consumer)无法更改流元素,因为这些元素是不可变的Java String 。 然而,如果流元件是可变的,则Consumer传递给peek(Consumer)可以改变这些元素的含量。 为了说明这一点,我将使用MutablePerson显示的简单类MutablePerson

MutablePerson.java

package dustin.examples.jdk8.streams;

/**
 * Represents person whose name can be changed.
 */
public class MutablePerson
{
   private String name;

   public MutablePerson(final String newName)
   {
      name = newName;
   }

   public String getName()
   {
      return name;
   }

   public void setName(final String newName)
   {
      name = newName;
   }

   @Override
   public String toString()
   {
      return name;
   }
}

下一个代码清单显示了当Stream.peek(Consumer)中的元素可变时, Stream.peek(Consumer)如何更改流操作的结果。

final List<MutablePerson> people
   = Stream.of(
      new MutablePerson("Fred"),
      new MutablePerson("Wilma"),
      new MutablePerson("Barney"),
      new MutablePerson("Betty"))
   .peek(person -> out.println(person))
   .peek(person -> person.setName(person.getName().toUpperCase()))
   .collect(Collectors.toList());
out.println("People: " + people);

执行上述代码后,它会产生如下所示的输出:

Fred
Wilma
Barney
Betty
People: [FRED, WILMA, BARNEY, BETTY]

此示例表明,传递给peekConsumer确实将人民姓名的大小写更改为大写。 这仅是可能的,因为正在处理的对象是可变的。 有人认为使用peek改变流中的元素可能是一种反模式 ,我发现自己对此方法感到不舒服(但我通常也不喜欢将方法的参数设为“ 输出参数 ”)。 peek方法的名称peek一个人只是看(而不是触摸),但是它接受的Consumer参数则宣告可以更改某些内容( Consumer的Javadoc指出,“与大多数其他功能接口不同,Consumer应该通过副作用来操作” )。 博客文章“ 使用Java Stream API进行Stream.peek(Consumer) ”讨论了使用Stream.peek(Consumer)和变异操作相关的潜在问题。

Steam.peek(Consumer)是了解流操作如何影响元素的有用工具。

翻译自: https://www.javacodegeeks.com/2018/06/peeking-inside-java-streams.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值