通常,这是一个英语单词,意思是:“如果你想要并行性,你就完全被软管化了”。它就在这个词中:积累 - 随着时间的推移而聚集。除了从头开始,并应用积累直到完成之外,没有办法把它做好。accumulator
但是,java通过添加2个要求来解决这个问题:
- 关联性。 必须产生与 相同的结果,其中 X 是累加器函数。
a X (b X c)
(a X b) X c
- 标识函数。 必须等于 ,其中 是传递给的恒等式,X 是累加器函数。
ident X a
a
ident
reduce
让我们以函数和标识为例,如果您的意图是求和列表,则满足这两个要求。(a, b) -> a + b
0
Java可以通过对任意项求和,然后对这些项的结果求和来并行化这一点。 可以通过以下方式求和:首先将列表分成两部分,然后将这两个子列表交给线程进行单独求和,然后对每个线程提供的答案求和。这意味着java将在流中的任意点开始多次累积,并将在任意点上应用标识作为其累积的一部分,并且如果您的标识对象本身是可变的,这将带来快速的问题。[1, 5, 9, 12]
基本上没有办法将可变对象的概念和java的功能结合起来。从根本上说,它的设计目的不是以这种方式工作的。identity
reduce
与总和示例相反:不是在累加器中修改 a,而是既不修改 a 也不修改 b;相反,它们被组合成新创建的第三个值,这就是你应该如何使用这种方法。(a, b) -> a + b
与某些其他语言相反,其他语言既不要求等于A,也不要求关联性,但根据定义,根本无法并行化它。foldLeft可以与可变状态一起使用。例如,下面是在伪代码中使用 foldLeft 求和的 impl:(请注意,此处用作可变整数):foldLeft
accumulatorFunction(ident, A)
new int[1]
int sum = stream.foldLeft(new int[1], (int[] a, int b) -> a[0] += b)[0];
这个概念(你的累加器函数的LHS总是一样的,即你的身份对象,当你沿着它移动时,它被修改以集成流中的每个值)与java的reduce不兼容,据我所知,java没有(简单的)方法来对流做这种事情。
因此:情况更糟!“线程安全”还不够好,它需要是不可变的。一旦它是不可变的,它就是线程安全的。
仅仅使标识对象不可变并在每次减少时返回一个新实例就足够了吗?
这不仅仅是“足够好”,这或多或少是唯一合理的使用方式。reduce