From e41fea88e755945879bc0a03e433689a84a3d76a Mon Sep 17 00:00:00 2001 From: "Tagir F. Valeev" Date: Mon, 8 Feb 2016 10:40:00 +0100 Subject: [PATCH] 8148115: Stream.findFirst for unordered source optimization Reviewed-by: psandoz --- .../classes/java/util/stream/FindOps.java | 21 ++++++++----- .../java/util/stream/LambdaTestHelpers.java | 13 +++++++- .../tests/java/util/stream/FindAnyOpTest.java | 21 +++++-------- .../java/util/stream/FindFirstOpTest.java | 30 ++++++++++++------- 4 files changed, 54 insertions(+), 31 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/util/stream/FindOps.java b/jdk/src/java.base/share/classes/java/util/stream/FindOps.java index 5926e79dd4d..45e1d697de0 100644 --- a/jdk/src/java.base/share/classes/java/util/stream/FindOps.java +++ b/jdk/src/java.base/share/classes/java/util/stream/FindOps.java @@ -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 @@ final class FindOps { */ private static final class FindOp implements TerminalOp { private final StreamShape shape; - final boolean mustFindFirst; + final int opFlags; final O emptyValue; final Predicate presentPredicate; final Supplier> sinkSupplier; @@ -129,7 +129,7 @@ final class FindOps { O emptyValue, Predicate presentPredicate, Supplier> 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 @@ final class FindOps { @Override public int getOpFlags() { - return StreamOpFlag.IS_SHORT_CIRCUIT | (mustFindFirst ? 0 : StreamOpFlag.NOT_ORDERED); + return opFlags; } @Override @@ -156,7 +156,10 @@ final class FindOps { @Override public O evaluateParallel(PipelineHelper helper, Spliterator 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 @@ final class FindOps { private static final class FindTask extends AbstractShortCircuitTask> { private final FindOp op; + private final boolean mustFindFirst; FindTask(FindOp op, + boolean mustFindFirst, PipelineHelper helper, Spliterator spliterator) { super(helper, spliterator); + this.mustFindFirst = mustFindFirst; this.op = op; } FindTask(FindTask parent, Spliterator spliterator) { super(parent, spliterator); + this.mustFindFirst = parent.mustFindFirst; this.op = parent.op; } @@ -283,7 +290,7 @@ final class FindOps { @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 @@ final class FindOps { @Override public void onCompletion(CountedCompleter caller) { - if (op.mustFindFirst) { + if (mustFindFirst) { for (FindTask child = leftChild, p = null; child != p; p = child, child = rightChild) { O result = child.getLocalResult(); diff --git a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LambdaTestHelpers.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LambdaTestHelpers.java index 76a4d72ad2a..dbeada29c55 100644 --- a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LambdaTestHelpers.java +++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LambdaTestHelpers.java @@ -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 java.util.function.ToLongFunction; 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 @@ public class LambdaTestHelpers { assertEquals(toBoxedMultiset(actual), toBoxedMultiset(expected)); } + public static void assertContains(Optional actual, Iterator 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 additionalInfo) { try { r.run(); diff --git a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FindAnyOpTest.java b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FindAnyOpTest.java index 64a9240fecd..6dd44206f17 100644 --- a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FindAnyOpTest.java +++ b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FindAnyOpTest.java @@ -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 @@ public class FindAnyOpTest extends OpTestCase { void exerciseStream(TestData.OfRef data, Function, Stream> fs) { Optional or = withData(data).terminal(fs, s -> s.findAny()).equalator(VALID_ANSWER).exercise(); - if (or.isPresent()) { - Integer r = or.get(); - Iterator 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) diff --git a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FindFirstOpTest.java b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FindFirstOpTest.java index 02e3d0727b8..40ec1cca108 100644 --- a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FindFirstOpTest.java +++ b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FindFirstOpTest.java @@ -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 @@ public class FindFirstOpTest extends OpTestCase { } void exerciseStream(TestData.OfRef data, Function, Stream> fs) { - Optional r = exerciseTerminalOps(data, fs, s -> s.findFirst()); - if (r.isPresent()) { - Iterator i = fs.apply(data.stream()).iterator(); - assertTrue(i.hasNext()); - assertEquals(i.next(), r.get()); - } - else { - assertFalse(fs.apply(data.stream()).iterator().hasNext()); - } + Iterator i = fs.apply(data.stream()).iterator(); + Optional 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)