【问题标题】:Side-effects in java 8 streamsjava 8 流中的副作用
【发布时间】:2017-02-28 23:07:16
【问题描述】:

Oracle 文档中有几处我无法完全理解:

Operations like forEach and peek are designed for side effects;

他们所说的“设计”是什么意思?这两个人有什么特别之处?我可以编写我的代码,以便任何流 API 方法都可以通过副作用工作。据我所知,这完全取决于我。更重要的是,我通常会使用 peek() 来修改元素的状态,而不是源本身,这不会使其有状态或容易产生副作用。

forEach 的 javadoc 还说:

The behavior of this operation is explicitly nondeterministic.

当他们对 findAny 说同样的话时,我理解这一点,但对于 forEach 有什么不确定性?如果流没有排序,则并行流中的任何操作都不能保证排序。为什么只针对 forEach(和 finAny,但它的含义有点不同)提到了不确定性?

for peek 和 forEach javadoc 还包含:

action may be performed at whatever time and in whatever thread the library chooses

再一次,为什么只有那里?为什么其他操作没有提到它?

【问题讨论】:

  • 嗯,其实你的问题有两点回答。对于普通的中间操作,不确定性是无关紧要的,因为它们应该没有副作用并且不干扰。对于forEach,这是“为副作用而设计的”,不确定性很重要,必须记录在案。
  • 如果你想要一个正确的答案,这取决于你。规范明确指出传递给大多数流方法的行为参数是无状态。这意味着,在存在可能影响计算的副作用的情况下,无法保证您得到正确的答案。
  • 所以,通过“为副作用而设计”,它们实际上意味着 peek 和 forEach 的实现中有一些东西可以使它们对副作用有弹性,但是其余的功能不能保证相同吗?

标签: java-8 java-stream side-effects


【解决方案1】:

首先,修改对象状态也是一个副作用,因为执行 lambda 后程序的整体状态会发生变化。如果您的并行流中有多次相同的对象,您甚至可能会遇到并发问题。无副作用函数是不改变程序状态的函数,其返回值仅基于其参数。由于其他流操作需要无副作用,因此无需提及它们将在哪个线程以及何时执行,因为这无关紧要。但如果允许操作有副作用,则应明确提及。

forEach 与 forEachOrdered 不同,即使流是无序的:forEachOrdered 始终保证其 lambda 不会同时在多个不同线程中同时执行。 forEach 调用不仅使您的流隐式无序,而且还消除了这种保证:您的 lambda 现在可以并发执行。由于 lambda 可能会产生副作用,因此应明确提及。

【讨论】:

  • “无副作用函数是一个不改变程序状态的函数,它的返回值仅基于它的参数”——这不是纯函数的定义吗?无副作用意味着:“局部静态变量、非局部变量、可变引用参数或 I/O 流没有突变” 一个函数可以没有副作用,但它的返回值仍然不是基于它的参数。例如: int f() { return x;这个函数没有副作用,但是它的返回值会随着一个非局部变量而变化。
猜你喜欢
  • 2019-01-16
  • 2016-09-14
  • 2023-04-10
  • 1970-01-01
  • 1970-01-01
  • 2015-12-14
  • 2020-01-05
  • 2016-10-09
  • 1970-01-01
相关资源
最近更新 更多