当.stream()。parallel()做同一件事时,为什么存在Collection.parallelStream()? - java

在Java 8中,用两个返回Stream<E>的方法扩展了Collection接口,其中stream()返回一个顺序流,而parallelStream()返回一个可能并行的流。 Stream本身也具有parallel()方法,该方法返回等效的并行流(将当前流更改为并行或创建新流)。

复制有明显的缺点:

令人困惑。给定parallelStream()可能返回顺序流的问题,因此问whether calling both parallelStream().parallel() is necessary to be sure the stream is parallel。如果不能保证parallelStream()为什么存在,为什么?另一种方法也是令人困惑的-如果parallelStream()返回顺序流,则可能是有原因的(例如,并行流本身就是性能陷阱的固有顺序数据结构); Stream.parallel()对这样的流应该做什么? (parallel()的规范不允许UnsupportedOperationException。)
如果现有实现的名称相似的方法具有不兼容的返回类型,则将方法添加到接口存在冲突的风险。除了stream()之外,添加parallelStream()会使获得很少收益的风险加倍。 (请注意,parallelStream()在某一时刻仅被命名为parallel(),尽管我不知道是否对其进行了重命名以避免名称冲突或其他原因。)

为什么在调用Collection.stream()。parallel()时存在Collection.parallelStream()同样的事情?

参考方案

Collection.(parallelS|s)tream()Stream的Javadocs本身无法回答问题,因此有关原理的信息已发送至邮件列表。我浏览了lambda-libs-spec-observers档案,找到了one thread specifically about Collection.parallelStream()和另一个线程,该线程触及java.util.Arrays should provide parallelStream()是否匹配(或者实际上是否应该删除)。没有一个一劳永逸的结论,所以也许我错过了另一个清单中的某些内容,或者此事在私人讨论中解决了。 (也许Brian Goetz是此讨论的原理之一,可以弥补任何遗漏的内容。)

参与者的观点很好,因此,答案基本上只是相关引语的组织,在[方括号]中有一些澄清,以重要性的顺序给出(据我解释)。

parallelStream()涵盖了一个非常常见的情况

第一个线程中的Brian Goetz,说明了即使删除了其他并行流工厂方法后,为什么Collections.parallelStream()仍然足以保留的价值:

我们没有每个[流工厂]的显式并行版本。我们做了
最初,为了减少API表面积,我们在
从API中删除20多种方法值得进行权衡的理论
.intRange(...).parallel()的表面皱纹和性能成本。
但是我们没有使用Collection做出选择。

我们可以删除Collection.parallelStream(),也可以添加
所有生成器的并行版本,否则我们什么也做不了,
保持原样。我认为所有这些在API设计方面都是合理的。

尽管不一致,但我还是喜欢现状。代替
有2N个流构建方法,我们有N + 1个-但是那额外的1个
涵盖了很多情况,因为它是每个人都继承的
采集。所以我可以为自己辩解为什么要有这种额外的1种方法
是值得的,为什么接受不再走一步的矛盾是
可以接受的。

别人不同意吗? N + 1 [仅适用于Collections.parallelStream()]是这里的实际选择吗?还是我们应该去
N的纯度[依靠Stream.parallel()]?还是2N(所有工厂的并行版本)的便利性和一致性?或者是
还有一些更好的N + 3 [Collections.parallelStream()加上其他特殊情况],对于其他一些特殊选择的情况,我们
想给予特别支持吗?

Brian Goetz在后面有关Arrays.parallelStream()的讨论中坚持这一立场:

我还是很喜欢Collection.parallelStream;它有巨大的
可发现性的优势,并在API上带来可观的回报
表面积-另一种方法,但可以在很多地方提供价值,
因为Collection是流源的真正常见情况。

parallelStream()性能更高

Brian Goetz:

直接版本[parallelStream()]的性能更高,因为它需要更少的包装(
将流变成并行流,您必须先创建
顺序流,然后将其状态的所有权转移到新
流。)

为了回应Kevin Bourrillion对效果是否显着的怀疑,Brian again:

取决于您计数的认真程度。道格计算单个对象
在进行并行操作的过程中进行创建和虚拟调用,
因为在开始分叉之前,您处于Amdahl的错误立场
法则-这是在分叉之前发生的所有“序列分数”
任何工作,将您的收支平衡极限进一步提高。所以得到
快速并行操作的设置路径很有价值。

Doug Lea follows up,但对冲他的位置:

处理并行库支持的人员需要一些态度
调整这类事情。在即将成为典型的机器上,
您浪费的每个周期设置并行度都需要花费64个周期。
如果需要64,您可能会有不同的反应
对象创建以开始并行计算。

也就是说,我始终完全支持强制实施者
为了更好的API而努力工作,只要
API并不排除有效的实现。所以如果杀死
parallelStream非常重要,我们将找到一些方法来
stream().parallel()变成位翻转或类似方式。

确实,稍后有关Arrays.parallelStream() takes notice of lower Stream.parallel() cost的讨论。

stream()。parallel()有状态使未来复杂化

在讨论时,可以将流从顺序切换到并行再切换回去可以与其他流操作交错进行。 Brian Goetz, on behalf of Doug Lea,解释了为什么顺序/并行模式切换会使Java平台的未来开发变得复杂:

我将竭尽全力解释原因:因为它(像有状态的
您也不喜欢的方法(排序,不同,限制))
距离能够表达流管道越来越远
传统的数据并行构造的术语,这进一步限制了
我们将它们直接映射到明天的计算基础上的能力,
无论是矢量处理器,FPGA,GPU还是我们自己准备的东西。

Filter-map-reduce映射非常干净地用于各种并行计算
基材过滤器并行映射顺序排序限制并行映射uniq减少
才不是。

因此,这里的整个API设计在设计之间体现出许多张力
易于表达用户可能想要表达的事物,并且正在做
可以使我们以透明的成本快速实现预期目标
楷模。

此模式切换为removed after further discussion。在该库的当前版本中,流管道是顺序的或并行的。对sequential() / parallel()的最后一次呼叫获胜。除了回避状态问题之外,此更改还提高了使用parallel()从顺序流工厂建立并行管道的性能。

将parallelStream()公开为一等公民可以提高程序员对库的认识,从而使他们编写更好的代码

Brian Goetz again,作为对Tim Peierls's argument的回应,Stream.parallel()允许程序员在并行之前顺序地了解流:

我对此顺序的价值有一些不同的看法
直觉-我认为普遍的“顺序期望”是一种
这整个工作的最大挑战;人们不断
带来不正确的顺序偏见,这会使他们变得愚蠢
例如使用单元素数组来“欺骗”“愚蠢”的方法
编译器让他们捕获可变的本地变量,或者使用lambda作为
用于映射将在
计算(以非线程安全的方式),然后指出
他们在做什么,耸耸肩说:“是的,但我没有
并行进行。”

我们进行了很多设计折衷,以合并顺序和并行
流。我认为,结果是干净的,并将增加
图书馆在十年以上仍然有用的机会,但我没有
特别像鼓励人们认为这是一个
顺序库,侧面装有一些平行袋。

JAVA 8具有任何匹配属性的对象的过滤器列表 - java

我的要求是通过匹配任何属性的字符串来过滤对象列表。例如,假设Contact类具有三个属性:街道,城市,电话。我知道java流过滤器是如何工作的,在这里我必须将输入字符串与每个属性进行比较,如下所示:contactList.stream().filter(contact -> contact.getStreet().equals("dubai&…

检查Optional中是否存在null属性,并返回String Java Stream API - java

我有以下class Person private String firstName; private String familyName; // Setters and Getters 我有以下方法public String getFullName(Optional<Person> persons) { return persons .map(p…

Java 8 Stream从过滤结果中获取对象 - java

注意:我不知道我的标题是否正确,所以请随时将其更改为更合适的名称,因为Java 8的术语是我的新手。问题:我有一些对象A,我想根据它持有的数值(例如整数)对其进行过滤。我想找到具有最高值的对象,然后返回该对象。使用流如何完成?public SomeObject getObjectWithHighestValue() { int max = Integer.M…

为什么我不能在Java中重写方法wait()? - java

Improve this question 我在类wait()中找到了方法Object。这是最终的,这意味着该方法不能被覆盖。有什么想法为什么是最终的? 参考方案 @Flavio-这实际上是一个很好的问题。当然,您不能覆盖它的原因是设计师将其“确定为最终”。做出此决定的一些潜在原因:您不希望人们弄乱基本类(“对象”类)上基本操作的语义。由于它是“最终的”,因…

Web应用程序上的恶意用户是否可以操纵Web应用程序前端发送的输入(在表单数据旁边)? - java

Web应用程序上的恶意用户是否可以通过任何可能的方式来操纵Web应用程序前端发送的输入(当然,这不是在谈论FORM DATA),但是发送的请求例如当我允许他编辑他的个人资料或他的内容时,他可能会操纵ID(userId或contentId),从而可能恶意地对其他用户的内容进行邪恶?这些输入固定在网页上并且不可编辑,但用户仍然可以操纵它们吗?用户是否可能以这种方…