在哪些情况下 Stream 操作应该是有状态的?

FBB*_*FBB 5 java java-8 java-stream

javaodoc 中stream,在本节的末尾Parallelism,我读到:

大多数流操作接受描述用户指定行为的参数,这些参数通常是 lambda 表达式。为了保持正确的行为,这些行为参数必须是无干扰的,并且在大多数情况下必须是无状态的

我很难理解这个“在大多数情况下”。在哪些情况下可以接受/需要有状态的流操作?

我的意思是,我知道这是可能的,特别是在使用顺序流时,但同一个 javadoc 明确指出:

除了明确标识findAny()为非确定性的操作,例如,流是顺序执行还是并行执行不应改变计算结果。

并且:

另请注意,尝试从行为参数访问可变状态会给您带来安全和性能方面的错误选择;[...] 最好的方法是避免有状态的行为参数完全流式传输操作;通常有一种方法可以重构流管道以避免有状态。

所以,我的问题是:在哪种情况下使用有状态的流操作是一个好习惯(而不是用于副作用工作的方法,例如forEach)?

一个相关的问题可能是:为什么有副作用的操作,例如forEach?我总是最终做一个很好的旧for循环,以避免在我的 lambda 表达式中产生副作用。

And*_*eas 3

有状态流 lambda 的示例:

  • collect(Collector)Collector根据定义,它是有状态的,因为它必须收集集合(状态)中的所有元素。
  • forEach(Consumer)Consumer根据定义,它是有状态的,除非它是黑洞(无操作)。
  • peek(Consumer)Consumer:根据定义,它是有状态的,因为如果不将其存储在某个地方(例如日志),为什么要查看呢?

因此,CollectorConsumer是两个 lambda 接口,根据定义,它们是有状态的。

所有其他的,例如PredicateFunctionUnaryOperatorBinaryOperatorComparator应该是无状态的。

  • 我不认为 `peek(x->log(x))` 在这种情况下会被认为是“有状态的”。在“map(func)”的“func”中插入日志记录也不会被视为有状态。“有状态”这个词在这里需要更好的定义。 (4认同)
  • “collect”不必设计为有状态的;即“accumulator”可以是一个“(A,T)->T”函数,它可以是无状态的。这可能是由于一些实际的考虑,例如能够将 `List::add` 写为累加器...... (2认同)
  • 您似乎将“有状态”与“有副作用”混淆了。 (2认同)