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您给出的所有示例都是针对可交换的累加器函数的(除关联函数外)。如果操作是可交换的,“初始值”是幂等的,那么并行归约将等价于顺序归约(我认为)。