Java 流归约恒等式与幂等值

Java 流归约恒等式与幂等值,java,parallel-processing,java-stream,reduction,idempotent,Java,Parallel Processing,Java Stream,Reduction,Idempotent,程序包文档给出了还原上下文中标识的定义: 标识值必须是组合器功能的标识。这意味着对于所有u,combiner.apply(identity,u)等于u 它的原始对应物提供了类似的定义 据我所知,这个定义对于支持并行流是必要的。例如,求和减少的非零种子值可以乘以并行处理器的数量,从而不可预测地扭曲最终结果 但这个定义似乎比支持并行性所必需的更严格。为什么不需要一个值x,这样combiner.apply(x,x)就等于x?这将保护像Integer::sum、(a,b)->a*b和String::co

程序包文档给出了还原上下文中标识的定义:

标识
值必须是组合器功能的标识。这意味着对于所有
u
combiner.apply(identity,u)
等于
u

它的原始对应物提供了类似的定义

据我所知,这个定义对于支持并行流是必要的。例如,求和减少的非零种子值可以乘以并行处理器的数量,从而不可预测地扭曲最终结果

但这个定义似乎比支持并行性所必需的更严格。为什么不需要一个值
x
,这样
combiner.apply(x,x)
就等于
x
?这将保护像
Integer::sum
(a,b)->a*b
String::concat
这样的函数不在多个进程之间倾斜,同时仍然允许使用具有幂等函数的任何种子,如
Math::max
(a,b)->a | b


我忽略了身份值有什么独特的好处吗?

为什么
combiner.apply(x,x)=x
是身份的一个好定义

假设组合器为
max()
。当然,
max(x,x)==x
为真。对于任何
x
,这实际上都是正确的,这意味着对于
max()
,任何值都是一个好的
identity

这当然不是真的,因为组合器的唯一有效标识值是
max()
最小值(或者是一个“无值”,例如
null
NaN
,假设组合器理解并忽略这样一个值)。

仅问“为什么不?”并提供一些不会导致问题的示例是不够的。您必须提供一个证据,证明在这个宽松的需求下,顺序和并行评估可以保证产生相同的正确结果

让我们使用2参数reduce来简化问题。让我们也将自己限制为一个包含2个元素(a,b)的流。让恒等式为i,累加器为op

当按顺序计算时,结果为(i op a)op b。当并行计算时,它可能是(iop a)op(iop b)(或者中间甚至可能有一个裸露的i,但现在我们不必担心)。我们希望这两者都等于a,op,b

如果我被要求是一个身份,很容易看出顺序和并行评估都是正确的。但如果我们对i的所有要求是i op i=i,那么显然,对于任何结合op和任何a和b,它并不意味着(i op a)op(i op b)必须等于a op b


我能想到的一个反例是空间折叠连接:
(x,y)->(x+y).replaceAll(“+”,“”)
。它是关联的,
是幂等的,但不是与此函数相关的恒等式。平行和连续的评估将产生不同的结果。

我不是在建议一个身份的替代定义。我是说,我们不需要如此严格的规则来支持并行缩减。换句话说,为什么不允许任何值作为
max()
的种子?因为如果流只包含小于5的值,则种子值(例如
5
)是不好的。它与并行处理无关。要求将
标识
值包含在给定给组合器的值集中,不会影响结果。例如,只有一个标识值与原语加法无关:
0
。原语乘法只有一个:
1
。为什么它“不好”?当然,这会影响结果,但这是意料之中的。回答不错,但我不禁想知道是否有一些规则适用于我给出的示例。@shmosel您给出的所有示例都是针对可交换的累加器函数的(除关联函数外)。如果操作是可交换的,“初始值”是幂等的,那么并行归约将等价于顺序归约(我认为)。