如何在Java8流中管理附带效果

gga*_*zor 3 java functional-programming java-8 java-stream

据我所知,Java8中的Streams只能收集一次(如在forEach()终止中),但我想知道我们是否可以设法在一系列过滤和映射任务中间"挖掘"到流中以产生一些附带效果.

从本质上讲,"点击"就像在做一个forEach()但是在管道的那个点返回Stream.

我会像这样使用它:

List<User> createAndPersistRandomUsers(int count) {
  return IntStream.range(0, count)
    .boxed() // returns Stream<Integer>
    .map(UserBuilder::random) // returns Stream<User>
    .tap(userRepo::persist) // Collateral persist of user, returns Stream<User>
    .collect(toList()); // returns List<User>
}
Run Code Online (Sandbox Code Playgroud)

有任何想法吗?

Era*_*ran 6

我能想到达到你想要的最接近的东西是偷看:

Stream Peek(消费者行动)

返回由此流的元素组成的流,此外还在从结果流中消耗元素时对每个元素执行提供的操作.

这是一个中间操作.

它允许您在Stream消耗时对Stream的元素执行操作.

List<User> createAndPersistRandomUsers(int count) {
  return IntStream.range(0, count)
    .boxed() // returns Stream<Integer>
    .map(UserBuilder::random) // returns Stream<User>
    .peek(userRepo::persist) // Collateral persist of user, returns Stream<User>
    .collect(toList()); // returns List<User>
}
Run Code Online (Sandbox Code Playgroud)

  • 值得注意的是,这不是一个"forEach"类似行动的问题,因为"peek"将只处理项目,因为它们恰好被处理以适应终端操作.例如,如果终端操作是`findAny`,那么你可以确定`peek`将看到的唯一项是匹配项,所有其他项可能会被跳过.这甚至可以以依赖于实现的方式改变,例如,当终端操作是"count"时,当前实现处理所有项目,如果底层的"Spliterator"具有"SIZED"特征,则未来版本可以跳过整个处理. (4认同)
  • 是的,它在这里起作用,这就是为什么我只是说"值得注意",因为我怀疑否则一旦OP改变终端操作,我们就会在SO上有一个后续问题. (3认同)
  • @Holger我同意,但对于这个问题中的用例(终端操作处理Stream的所有元素)我认为它产生了预期的行为(即列表中返回的每个用户也将作为副作用持久化当然,这样做并没有真正的优势,而不是先生成列表,然后坚持所有用户. (2认同)