8148115: Stream.findFirst for unordered source optimization
authortvaleev
Mon, 08 Feb 2016 10:40:00 +0100
changeset 35713 f61cb8475e5a
parent 35712 db033bd15d41
child 35714 05eadb5e7022
8148115: Stream.findFirst for unordered source optimization Reviewed-by: psandoz
jdk/src/java.base/share/classes/java/util/stream/FindOps.java
jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LambdaTestHelpers.java
jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FindAnyOpTest.java
jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FindFirstOpTest.java
--- a/jdk/src/java.base/share/classes/java/util/stream/FindOps.java	Mon Feb 08 10:37:37 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/util/stream/FindOps.java	Mon Feb 08 10:40:00 2016 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -107,7 +107,7 @@
      */
     private static final class FindOp<T, O> implements TerminalOp<T, O> {
         private final StreamShape shape;
-        final boolean mustFindFirst;
+        final int opFlags;
         final O emptyValue;
         final Predicate<O> presentPredicate;
         final Supplier<TerminalSink<T, O>> sinkSupplier;
@@ -129,7 +129,7 @@
                        O emptyValue,
                        Predicate<O> presentPredicate,
                        Supplier<TerminalSink<T, O>> sinkSupplier) {
-            this.mustFindFirst = mustFindFirst;
+            this.opFlags = StreamOpFlag.IS_SHORT_CIRCUIT | (mustFindFirst ? 0 : StreamOpFlag.NOT_ORDERED);
             this.shape = shape;
             this.emptyValue = emptyValue;
             this.presentPredicate = presentPredicate;
@@ -138,7 +138,7 @@
 
         @Override
         public int getOpFlags() {
-            return StreamOpFlag.IS_SHORT_CIRCUIT | (mustFindFirst ? 0 : StreamOpFlag.NOT_ORDERED);
+            return opFlags;
         }
 
         @Override
@@ -156,7 +156,10 @@
         @Override
         public <P_IN> O evaluateParallel(PipelineHelper<T> helper,
                                          Spliterator<P_IN> spliterator) {
-            return new FindTask<>(this, helper, spliterator).invoke();
+            // This takes into account the upstream ops flags and the terminal
+            // op flags and therefore takes into account findFirst or findAny
+            boolean mustFindFirst = StreamOpFlag.ORDERED.isKnown(helper.getStreamAndOpFlags());
+            return new FindTask<>(this, mustFindFirst, helper, spliterator).invoke();
         }
     }
 
@@ -250,16 +253,20 @@
     private static final class FindTask<P_IN, P_OUT, O>
             extends AbstractShortCircuitTask<P_IN, P_OUT, O, FindTask<P_IN, P_OUT, O>> {
         private final FindOp<P_OUT, O> op;
+        private final boolean mustFindFirst;
 
         FindTask(FindOp<P_OUT, O> op,
+                 boolean mustFindFirst,
                  PipelineHelper<P_OUT> helper,
                  Spliterator<P_IN> spliterator) {
             super(helper, spliterator);
+            this.mustFindFirst = mustFindFirst;
             this.op = op;
         }
 
         FindTask(FindTask<P_IN, P_OUT, O> parent, Spliterator<P_IN> spliterator) {
             super(parent, spliterator);
+            this.mustFindFirst = parent.mustFindFirst;
             this.op = parent.op;
         }
 
@@ -283,7 +290,7 @@
         @Override
         protected O doLeaf() {
             O result = helper.wrapAndCopyInto(op.sinkSupplier.get(), spliterator).get();
-            if (!op.mustFindFirst) {
+            if (!mustFindFirst) {
                 if (result != null)
                     shortCircuit(result);
                 return null;
@@ -300,7 +307,7 @@
 
         @Override
         public void onCompletion(CountedCompleter<?> caller) {
-            if (op.mustFindFirst) {
+            if (mustFindFirst) {
                     for (FindTask<P_IN, P_OUT, O> child = leftChild, p = null; child != p;
                          p = child, child = rightChild) {
                     O result = child.getLocalResult();
--- a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LambdaTestHelpers.java	Mon Feb 08 10:37:37 2016 +0100
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LambdaTestHelpers.java	Mon Feb 08 10:40:00 2016 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -47,6 +47,7 @@
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertFalse;
 
 /**
  * LambdaTestHelpers -- assertion methods and useful objects for lambda test cases
@@ -400,6 +401,16 @@
         assertEquals(toBoxedMultiset(actual), toBoxedMultiset(expected));
     }
 
+    public static<T> void assertContains(Optional<T> actual, Iterator<T> it) {
+        actual.ifPresentOrElse(r -> {
+            boolean contained = false;
+            while (!contained && it.hasNext()) {
+                contained = Objects.equals(r, it.next());
+            }
+            assertTrue(contained, "Not found: "+r);
+        }, () -> assertFalse(it.hasNext()));
+    }
+
     public static void launderAssertion(Runnable r, Supplier<String> additionalInfo) {
         try {
             r.run();
--- a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FindAnyOpTest.java	Mon Feb 08 10:37:37 2016 +0100
+++ b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FindAnyOpTest.java	Mon Feb 08 10:40:00 2016 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -20,6 +20,12 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
+
+/**
+ * @test
+ * @bug 8148115
+ */
+
 package org.openjdk.tests.java.util.stream;
 
 import java.util.*;
@@ -61,18 +67,7 @@
 
     void exerciseStream(TestData.OfRef<Integer> data, Function<Stream<Integer>, Stream<Integer>> fs) {
         Optional<Integer> or = withData(data).terminal(fs, s -> s.findAny()).equalator(VALID_ANSWER).exercise();
-        if (or.isPresent()) {
-            Integer r = or.get();
-            Iterator<Integer> it = fs.apply(data.stream()).iterator();
-            boolean contained = false;
-            while (!contained && it.hasNext()) {
-                contained = Objects.equals(r, it.next());
-            }
-            assertTrue(contained);
-        }
-        else {
-            assertFalse(fs.apply(data.stream()).iterator().hasNext());
-        }
+        assertContains(or, fs.apply(data.stream()).iterator());
     }
 
     @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class)
--- a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FindFirstOpTest.java	Mon Feb 08 10:37:37 2016 +0100
+++ b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FindFirstOpTest.java	Mon Feb 08 10:40:00 2016 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -20,6 +20,12 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
+
+/**
+ * @test
+ * @bug 8148115
+ */
+
 package org.openjdk.tests.java.util.stream;
 
 import java.util.*;
@@ -59,15 +65,19 @@
     }
 
     void exerciseStream(TestData.OfRef<Integer> data, Function<Stream<Integer>, Stream<Integer>> fs) {
-        Optional<Integer> r = exerciseTerminalOps(data, fs, s -> s.findFirst());
-        if (r.isPresent()) {
-            Iterator<Integer> i = fs.apply(data.stream()).iterator();
-            assertTrue(i.hasNext());
-            assertEquals(i.next(), r.get());
-        }
-        else {
-            assertFalse(fs.apply(data.stream()).iterator().hasNext());
-        }
+        Iterator<Integer> i = fs.apply(data.stream()).iterator();
+        Optional<Integer> expected = i.hasNext() ? Optional.of(i.next()) : Optional.empty();
+        withData(data).terminal(fs, s -> s.findFirst())
+                      .expectedResult(expected)
+                      .resultAsserter((act, exp, ord, par) -> {
+                          if (par & !ord) {
+                              assertContains(act, fs.apply(data.stream()).iterator());
+                          }
+                          else {
+                              assertEquals(act, exp);
+                          }
+                      })
+                      .exercise();
     }
 
     @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class)