当前位置: 首页 > 面试题库 >

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

江子石
2023-03-14
问题内容

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

复制有明显的缺点:

  • 令人困惑。提出一个问题,假设parallelStream()可能返回顺序流,是否必须同时调用parallelStream()。parallel()以确保流是并行的。如果不能保证parallelStream()为什么存在,为什么?另一种方法也是令人困惑的-如果parallelStream()返回顺序流,则可能是有原因的(例如,固有顺序数据结构中并行流是性能陷阱);Stream.parallel()对这样的流应该做什么?(parallel()的规范不允许UnsupportedOperationException。)

  • 如果现有实现的名称相似的方法具有不兼容的返回类型,则将方法添加到接口存在冲突的风险。除了stream()之外,添加parallelStream()会使获得很少收益的风险加倍。(请注意,parallelStream()在某一时刻仅被命名为parallel(),尽管我不知道是否对其进行了重命名以避免名称冲突或其他原因。)

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


问题答案:

的Javadoc
Collection.(parallelS|s)tream()Stream本身不回答这个问题,所以它的关闭为理由的邮件列表。我浏览了lambda-
libs-spec-
observers档案,找到了一个专门关于Collection.parallelStream()的线程,另一个发现了java.util.Arrays是否应该提供parallelStream()进行匹配(或者实际上是否应该匹配)的线程。删除)。没有一劳永逸的结论,所以也许我错过了另一个清单中的某些内容,或者此事在私人讨论中解决了。

参与者的观点很清楚,因此,答案基本上只是相关引语的组织,并在 [方括号]中做 了一些说明,并按重要性顺序排列(据我解释)。

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

Brian Goetz在第一个线程中,解释了为什么Collections.parallelStream()即使删除了其他并行流工厂方法之后,仍然有价值的原因:

对于每个 [stream factory] ,我们都 没有
明确的并行版本;我们最初是这样做的,为了减少API的表面积,我们将其削减的理论是,从API中删除20种以上的方法值得在表面的皱纹和性能成本之间进行权衡。但是我们没有使用Collection做出选择。
__.intRange(...).parallel()

我们可以删除Collection.parallelStream(),也可以添加所有生成器的并行版本,或者什么也不做,将其保留不变。我认为所有这些在API设计方面都是合理的。

尽管有点不一致,但我还是很喜欢现状。不是使用2N个流构建方法,而是使用N +
1个,但是额外的1个覆盖了很多情况,因为它是每个Collection都继承的。因此,我可以为自己辩解,为什么拥有额外的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()]
的性能更高,因为它需要更少的包装(要将流转换成并行流,您必须先创建顺序流,然后将其状态的所有权转移到新的Stream中。)

为了回应KevinBourrillion对效果是否显着的怀疑,Brian再次:

取决于您计数的认真程度。道格在进行并行操作的过程中会计算单个对象的创建和虚拟调用,因为在开始分叉之前,您就处在阿姆达尔定律的错误一侧–这是发生在可以分叉任何工作之前的“序列分数”这将您的盈亏平衡点进一步推高。因此,快速获取并行操作的设置路径很有价值。

道格·李(Doug Lea)跟进,但对冲了他的位置:

处理并行库支持的人们需要对这种事情进行一些态度上的调整。在即将成为典型机器的计算机上,您浪费的每个周期设置并行性的成本为64个周期。如果它需要创建64个对象来启动并行计算,您可能会有不同的反应。

就是说,只要API不排除有效的实现,我总是完全支持强迫实现者为更好的API而更加努力。因此,如果杀戮
parallelStream真的很重要,我们将找到某种方法来使stream().parallel()位翻转或类似现象。

确实,以后的讨论Arrays.parallelStream()
注意到了更低的Stream.parallel()成本。

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

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

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

Filter-map-reduce可以非常清晰地映射到各种并行计算基板上;过滤器并行映射顺序排序限制极限并行映射uniq减少没有。

因此,这里的整个API设计在简化表达用户可能想要表达的内容与以一种可以通过透明成本模型快速预测的方式进行表达之间存在许多张力。

此模式切换在进一步讨论后被删除。在该库的当前版本中,流管道是顺序的或并行的。最后一次致电sequential()/
parallel()获胜。除了回避状态问题之外,此更改还提高了parallel()用于从顺序流工厂建立并行管道的性能。

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

Brian Goetz再次回应了Tim
Peierls的论点,该论点Stream.parallel()允许程序员在并行化之前顺序地理解流:

对于这种顺序直觉的价值,我的看法略有不同。我认为普遍的“顺序期望”是整个努力中最大的挑战之一。人们 不断
带来不正确的顺序偏差,这导致他们做一些愚蠢的事情,例如使用单元素数组来“欺骗”“愚蠢”的编译器以使其捕获可变的局部变量,或者使用lambda作为映射参数将会在计算过程中使用的一种突变状态(以一种非线程安全的方式),然后,当它指出他们正在做什么时,耸耸肩说:“是的,但是我没有这样做在平行下。”

我们进行了很多设计折衷,以合并顺序流和并行流。我相信这样做的结果是干净的,并且会增加该库在10年以上仍然有用的机会,但是我并不特别喜欢鼓励人们认为这是一个连续的库,上面钉着一些平行的书包的想法。在一边。



 类似资料:
  • 在Java8中,使用两个返回的方法扩展了集合接口:(返回顺序流)和(返回可能并行的流)。Stream本身也有一个方法,该方法返回一个等效的并行流(或者将当前流改为并行流,或者创建一个新流)。 重复有明显的缺点: 如果现有实现具有一个具有不兼容返回类型的类似名称的方法,则向接口添加方法可能会发生冲突。在stream()的基础上添加parallelStream()会使风险增加一倍,但收益很小。(请注意

  • output指示在1s暂停之前执行16个流元素,然后再执行16个元素,依此类推。因此,即使forkjoinpool是用100个线程创建的,也只有16个线程被使用。 当我使用超过23个线程时,就会出现这种模式:

  • 问题内容: 我有一个简单的函数,我将其称为。它使用两个参数,对其进行一些计算,然后返回结果。 我也有一个类,其中包含一个具有如下标头的构造函数: 当我尝试运行此类中的任何内容时,出现以下错误: 如果删除此类,则可以在Python Shell中使用myFun,那怎么办? 问题答案: 您尚未显示实际代码,因此很难确定,但是我敢肯定在之后定义。定义方法时将评估默认值表达式,因此必须在此时定义。以后定义它

  • 问题内容: 在Java 8中,我们有类,它奇怪地有一个方法 因此,您可能希望它实现接口,而该接口正是需要此方法,但事实并非如此。 当我想使用foreach循环遍历Stream时,我必须做类似的事情 我在这里想念什么吗? 问题答案: 人们已经在邮件列表 asked 上问过同样的问题。主要原因是Iterable也具有可重复的语义,而Stream没有。 我认为主要原因是Iterable暗示可重用性,而S

  • 在流处理过程中遇到运行时异常时,流处理是否应该中止?它应该先结束吗?是否应在?异常是按原样重新生成还是已包装?的JavaDoc和java包。util。小溪对此无话可说。 我发现有关Stackoverflow的所有问题似乎都集中在如何从函数接口中包装一个已检查的异常,以使其代码能够编译。事实上,互联网上的博客帖子和类似文章都关注相同的警告。这与我无关。 根据我自己的经验,一旦抛出,序列流的处理就会中

  • 我正在使用JdbcTemplate。query(sql、args、rowMapper)方法调用以返回对象列表。在某些情况下,我希望跳过一行,而不将其添加到返回的列表中。在这些情况下,我想到了两种解决方案: 让行映射器返回null 让RowMapper抛出一个异常(我知道SQLExceptions已被处理,所以这是一种可能性) 我的问题是:当返回null时,JdbcTemboard是否将其添加到列表