Stream.reduce始终保持并行,无序流的顺序

时间:2017-08-04 21:20:04

标签: java sorting java-8 java-stream reduce

我已经完成了以前的几个问题,例如Br​​ian Goetz的Encounter order preservation in java streamthis回答,以及Stream.reduce()的javadoc和java。 util.stream包javadoc,但我仍然无法掌握以下内容:

拿这段代码:

  public static void main(String... args) {
    final String[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
    System.out.println("Alphabet: ".concat(Arrays.toString(alphabet)));
    System.out.println(new HashSet<>(Arrays.asList(alphabet))
          .parallelStream()
          .unordered()
          .peek(System.out::println)
          .reduce("", (a,b) -> a + b, (a,b) -> a + b));
  }

为什么减少总是*保留遭遇顺序?

  • 到目前为止,经过几十次运行后,输出是相同的

3 个答案:

答案 0 :(得分:4)

首先unordered并不意味着实际的改组;它只是为Stream管道设置了一个标志 - 以后可以利用它。

源元素的混乱可能比流管道本身的操作要昂贵得多,因此实现可能选择不这样做(就像在这种情况下)。

目前<{em>}(已测试并查看来源)jdk-8jdk-9 - reduce并未将其考虑在内。请注意,这可能会在将来的构建或发布中发生变化。

另外,当您说unordered时 - 您实际上意味着您不关心该订单并且返回相同结果的流不违反该规则。

例如通知this问题/答案解释了findFirst例如(只是另一个终端操作)改为在java-9中考虑unordered而不是java-8 。

答案 1 :(得分:3)

为了帮助解释这一点,我将把这个字符串的范围缩小到ABCD

并行流将字符串分为两部分:ABCD。当我们稍后将它们组合起来时,AB侧的结果将是传递给函数的第一个参数,而CD侧的结果将是传递给函数的第二个参数。这与两者中的哪一个实际上完成无关。

unordered运算符会影响流上的某些操作,例如limit操作,它不会影响简单的reduce

答案 2 :(得分:0)

TLDR:.reduce()并不总是保持顺序,其结果基于流分离器的特性。

分散器

信息流的遇到顺序取决于信息流分隔符(之前没有提到任何答案)。

根据源流有不同的分隔符。您可以从这些集合的源代码中获取分隔符的类型。

HashSet -> HashMap#KeySpliterator =未排序

ArrayDeque =已订购

ArrayList =已订购

TreeSet -> TreeMap#Spliterator =已排序并已排序

logicbig.com - Ordering logicbig.com - Stateful vs Stateless

此外,您可以应用.unordered()中间流操作,该操作指定流中的以下操作不应依赖于顺序。

受分隔符和.unordered()方法的使用影响的流操作(大多数是有状态的)是:

  • .findFirst()
  • .limit()
  • .skip()
  • .distinct()

基于流及其分隔符的order属性,这些操作将为我们提供不同的结果。

.peek()方法不考虑顺序,如果流是并行执行的,它将始终以无序方式打印/接收元素。

.reduce()

现在使用终端.reduce()方法。中间操作.unordered()对分隔符的类型没有任何影响(如@Eugene所述)。但重要的是,它仍然与源分隔器中的相同。如果源分隔符是有序的,则.reduce()的结果将是有序的;如果源是无序的.reduce()的结果将是无序的。

您正在使用新的HashSet<>(Arrays.asList(alphabet))来获取流的实例。其分隔符为无序。您对结果进行排序只是一个巧合,因为您使用单个字母字符串作为流的元素,而无序结果实际上是相同的。现在,如果您将其与数字混合或将其与小写和大写字母混合,则不再适用。例如,输入以下内容,第一个是您发布的示例的子集:

HashSet .reduce()-无序

"A","B","C","D","E","F" -> "ABCDEF"
"a","b","c","1","2","3","A","B","C" -> "a1Ab2Bc3C"
"Apple","Orange","Banana","Mango" -> "AppleMangoOrangeBanana"

TreeSet .reduce()-已排序,已排序

"A","B","C","D","E","F" -> "ABCDEF"
"a","b","c","1","2","3","A","B","C" -> "123ABCabc"
"Apple","Orange","Banana","Mango" -> "AppleBananaMangoOrange"

ArrayList .reduce()-已排序

"A","B","C","D","E","F" -> "ABCDEF"
"a","b","c","1","2","3","A","B","C" -> "abc123ABC"
"Apple","Orange","Banana","Mango" -> "AppleOrangeBananaMango"

您看到仅使用字母源流测试.reduce()操作会导致错误的结论。

答案是.reduce()并不总是保持顺序,其结果基于流分离器的特性。