java.util.stream
包文档在缩减的上下文中为 identity 提供了这个定义:
identity
值必须是组合器函数的标识。这意味着,对于所有u
,combiner.apply(identity, u)
等于u
。
Stream.reduce()
及其原始对应物提供了类似的定义。
据我了解,这个定义对于支持并行流是必要的。例如,减少总和的非零种子值可以乘以并行处理器的数量,从而无法预测地扭曲最终结果。
但是这个定义似乎比支持并行性更加严格。为什么不要求idempotent值x
,以使combiner.apply(x, x)
等于x
?这样可以保护Integer::sum
,(a, b) -> a * b
和String::concat
等函数不会偏移多个进程,同时仍允许使用具有幂等函数的任何种子,例如Math::max
,{{1} }和Sets::intersection
。
我忽略的身份价值是否有一些独特的好处?
答案 0 :(得分:4)
不足以问及为什么不这样做?"并提供一些不会导致问题的例子。您必须提供一个证明,即通过这种宽松的要求,可以保证顺序和并行评估能够产生相同的正确结果。
让我们使用2参数reduce来简化问题。我们还将自己限制为包含2个元素的流( a , b )。让身份 i ,累加器为 op 。
按顺序评估时,结果为(i op a)op b 。当并行评估时,它可能是(i op a)op(i op b)(或者它们之间甚至可能存在裸 i ,但是让我们来看看它们。现在不用担心了。)我们希望这两者都等于 a op b
如果 i 需要是一个标识,很容易看出顺序和并行评估都是正确的。但是,如果 i 所需的全部是 i op i = i ,那么它显然不会遵循(i op a)op(i op b)对于任何关联 op 以及任何 a 和 b ,必须等于 a op b 。
我能想到的一个反例是空间崩溃连接:(x, y) -> (x + y).replaceAll(" +", " ")
。它是关联的," "
是幂等的,但不是关于此函数的身份。并行和顺序评估将产生不同的结果。
答案 1 :(得分:0)
为什么combiner.apply(x, x) == x
能够很好地定义身份?
说组合器是max()
。当然,max(x, x) == x
是真的。对于任何x
实际上都是如此,这意味着任何值对于identity
来说都是一个很好的max()
值。
肯定不是这样,因为max()
合并器的唯一有效标识值为MIN_VALUE
(或“{无值”,例如null
或NaN
,假设组合者理解并忽略了这样的值。)