8072727: add variation of Stream.iterate() that's finite

Reviewed-by: psandoz, briangoetz
This commit is contained in:
Tagir F. Valeev 2016-03-03 10:06:16 +01:00
parent 2255be0267
commit 90adb4174f
10 changed files with 462 additions and 53 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2015, 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
@ -949,24 +949,100 @@ public interface DoubleStream extends BaseStream<Double, DoubleStream> {
*/
public static DoubleStream iterate(final double seed, final DoubleUnaryOperator f) {
Objects.requireNonNull(f);
final PrimitiveIterator.OfDouble iterator = new PrimitiveIterator.OfDouble() {
double t = seed;
Spliterator.OfDouble spliterator = new Spliterators.AbstractDoubleSpliterator(Long.MAX_VALUE,
Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL) {
double prev;
boolean started;
@Override
public boolean hasNext() {
public boolean tryAdvance(DoubleConsumer action) {
Objects.requireNonNull(action);
double t;
if (started)
t = f.applyAsDouble(prev);
else {
t = seed;
started = true;
}
action.accept(prev = t);
return true;
}
};
return StreamSupport.doubleStream(spliterator, false);
}
/**
* Returns a sequential ordered {@code DoubleStream} produced by iterative
* application of a function to an initial element, conditioned on
* satisfying the supplied predicate. The stream terminates as soon as
* the predicate returns false.
*
* <p>
* {@code DoubleStream.iterate} should produce the same sequence of
* elements as produced by the corresponding for-loop:
* <pre>{@code
* for (double index=seed; predicate.test(index); index = f.apply(index)) {
* ...
* }
* }</pre>
*
* <p>
* The resulting sequence may be empty if the predicate does not hold on
* the seed value. Otherwise the first element will be the supplied seed
* value, the next element (if present) will be the result of applying the
* function f to the seed value, and so on iteratively until the predicate
* indicates that the stream should terminate.
*
* @param seed the initial element
* @param predicate a predicate to apply to elements to determine when the
* stream must terminate.
* @param f a function to be applied to the previous element to produce
* a new element
* @return a new sequential {@code DoubleStream}
* @since 9
*/
public static DoubleStream iterate(double seed, DoublePredicate predicate, DoubleUnaryOperator f) {
Objects.requireNonNull(f);
Objects.requireNonNull(predicate);
Spliterator.OfDouble spliterator = new Spliterators.AbstractDoubleSpliterator(Long.MAX_VALUE,
Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL) {
double prev;
boolean started, finished;
@Override
public boolean tryAdvance(DoubleConsumer action) {
Objects.requireNonNull(action);
if (finished)
return false;
double t;
if (started)
t = f.applyAsDouble(prev);
else {
t = seed;
started = true;
}
if (!predicate.test(t)) {
finished = true;
return false;
}
action.accept(prev = t);
return true;
}
@Override
public double nextDouble() {
double v = t;
t = f.applyAsDouble(t);
return v;
public void forEachRemaining(DoubleConsumer action) {
Objects.requireNonNull(action);
if (finished)
return;
finished = true;
double t = started ? f.applyAsDouble(prev) : seed;
while (predicate.test(t)) {
action.accept(t);
t = f.applyAsDouble(t);
}
}
};
return StreamSupport.doubleStream(Spliterators.spliteratorUnknownSize(
iterator,
Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL), false);
return StreamSupport.doubleStream(spliterator, false);
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2015, 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
@ -885,28 +885,104 @@ public interface IntStream extends BaseStream<Integer, IntStream> {
* @param seed the initial element
* @param f a function to be applied to the previous element to produce
* a new element
* @return A new sequential {@code IntStream}
* @return a new sequential {@code IntStream}
*/
public static IntStream iterate(final int seed, final IntUnaryOperator f) {
Objects.requireNonNull(f);
final PrimitiveIterator.OfInt iterator = new PrimitiveIterator.OfInt() {
int t = seed;
Spliterator.OfInt spliterator = new Spliterators.AbstractIntSpliterator(Long.MAX_VALUE,
Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL) {
int prev;
boolean started;
@Override
public boolean hasNext() {
public boolean tryAdvance(IntConsumer action) {
Objects.requireNonNull(action);
int t;
if (started)
t = f.applyAsInt(prev);
else {
t = seed;
started = true;
}
action.accept(prev = t);
return true;
}
};
return StreamSupport.intStream(spliterator, false);
}
/**
* Returns a sequential ordered {@code IntStream} produced by iterative
* application of a function to an initial element, conditioned on
* satisfying the supplied predicate. The stream terminates as soon as
* the predicate returns false.
*
* <p>
* {@code IntStream.iterate} should produce the same sequence of elements
* as produced by the corresponding for-loop:
* <pre>{@code
* for (int index=seed; predicate.test(index); index = f.apply(index)) {
* ...
* }
* }</pre>
*
* <p>
* The resulting sequence may be empty if the predicate does not hold on
* the seed value. Otherwise the first element will be the supplied seed
* value, the next element (if present) will be the result of applying the
* function f to the seed value, and so on iteratively until the predicate
* indicates that the stream should terminate.
*
* @param seed the initial element
* @param predicate a predicate to apply to elements to determine when the
* stream must terminate.
* @param f a function to be applied to the previous element to produce
* a new element
* @return a new sequential {@code IntStream}
* @since 9
*/
public static IntStream iterate(int seed, IntPredicate predicate, IntUnaryOperator f) {
Objects.requireNonNull(f);
Objects.requireNonNull(predicate);
Spliterator.OfInt spliterator = new Spliterators.AbstractIntSpliterator(Long.MAX_VALUE,
Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL) {
int prev;
boolean started, finished;
@Override
public boolean tryAdvance(IntConsumer action) {
Objects.requireNonNull(action);
if (finished)
return false;
int t;
if (started)
t = f.applyAsInt(prev);
else {
t = seed;
started = true;
}
if (!predicate.test(t)) {
finished = true;
return false;
}
action.accept(prev = t);
return true;
}
@Override
public int nextInt() {
int v = t;
t = f.applyAsInt(t);
return v;
public void forEachRemaining(IntConsumer action) {
Objects.requireNonNull(action);
if (finished)
return;
finished = true;
int t = started ? f.applyAsInt(prev) : seed;
while (predicate.test(t)) {
action.accept(t);
t = f.applyAsInt(t);
}
}
};
return StreamSupport.intStream(Spliterators.spliteratorUnknownSize(
iterator,
Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL), false);
return StreamSupport.intStream(spliterator, false);
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 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
@ -879,24 +879,100 @@ public interface LongStream extends BaseStream<Long, LongStream> {
*/
public static LongStream iterate(final long seed, final LongUnaryOperator f) {
Objects.requireNonNull(f);
final PrimitiveIterator.OfLong iterator = new PrimitiveIterator.OfLong() {
long t = seed;
Spliterator.OfLong spliterator = new Spliterators.AbstractLongSpliterator(Long.MAX_VALUE,
Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL) {
long prev;
boolean started;
@Override
public boolean hasNext() {
public boolean tryAdvance(LongConsumer action) {
Objects.requireNonNull(action);
long t;
if (started)
t = f.applyAsLong(prev);
else {
t = seed;
started = true;
}
action.accept(prev = t);
return true;
}
};
return StreamSupport.longStream(spliterator, false);
}
/**
* Returns a sequential ordered {@code LongStream} produced by iterative
* application of a function to an initial element, conditioned on
* satisfying the supplied predicate. The stream terminates as soon as
* the predicate returns false.
*
* <p>
* {@code LongStream.iterate} should produce the same sequence of elements
* as produced by the corresponding for-loop:
* <pre>{@code
* for (long index=seed; predicate.test(index); index = f.apply(index)) {
* ...
* }
* }</pre>
*
* <p>
* The resulting sequence may be empty if the predicate does not hold on
* the seed value. Otherwise the first element will be the supplied seed
* value, the next element (if present) will be the result of applying the
* function f to the seed value, and so on iteratively until the predicate
* indicates that the stream should terminate.
*
* @param seed the initial element
* @param predicate a predicate to apply to elements to determine when the
* stream must terminate.
* @param f a function to be applied to the previous element to produce
* a new element
* @return a new sequential {@code LongStream}
* @since 9
*/
public static LongStream iterate(long seed, LongPredicate predicate, LongUnaryOperator f) {
Objects.requireNonNull(f);
Objects.requireNonNull(predicate);
Spliterator.OfLong spliterator = new Spliterators.AbstractLongSpliterator(Long.MAX_VALUE,
Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL) {
long prev;
boolean started, finished;
@Override
public boolean tryAdvance(LongConsumer action) {
Objects.requireNonNull(action);
if (finished)
return false;
long t;
if (started)
t = f.applyAsLong(prev);
else {
t = seed;
started = true;
}
if (!predicate.test(t)) {
finished = true;
return false;
}
action.accept(prev = t);
return true;
}
@Override
public long nextLong() {
long v = t;
t = f.applyAsLong(t);
return v;
public void forEachRemaining(LongConsumer action) {
Objects.requireNonNull(action);
if (finished)
return;
finished = true;
long t = started ? f.applyAsLong(prev) : seed;
while (predicate.test(t)) {
action.accept(t);
t = f.applyAsLong(t);
}
}
};
return StreamSupport.longStream(Spliterators.spliteratorUnknownSize(
iterator,
Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL), false);
return StreamSupport.longStream(spliterator, false);
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2015, 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
@ -29,7 +29,6 @@ import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Objects;
import java.util.Optional;
import java.util.Spliterator;
@ -1185,23 +1184,103 @@ public interface Stream<T> extends BaseStream<T, Stream<T>> {
*/
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
Objects.requireNonNull(f);
final Iterator<T> iterator = new Iterator<T>() {
@SuppressWarnings("unchecked")
T t = (T) Streams.NONE;
Spliterator<T> spliterator = new Spliterators.AbstractSpliterator<>(Long.MAX_VALUE,
Spliterator.ORDERED | Spliterator.IMMUTABLE) {
T prev;
boolean started;
@Override
public boolean hasNext() {
public boolean tryAdvance(Consumer<? super T> action) {
Objects.requireNonNull(action);
T t;
if (started)
t = f.apply(prev);
else {
t = seed;
started = true;
}
action.accept(prev = t);
return true;
}
};
return StreamSupport.stream(spliterator, false);
}
/**
* Returns a sequential ordered {@code Stream} produced by iterative
* application of a function to an initial element, conditioned on
* satisfying the supplied predicate. The stream terminates as soon as
* the predicate returns false.
*
* <p>
* {@code Stream.iterate} should produce the same sequence of elements as
* produced by the corresponding for-loop:
* <pre>{@code
* for (T index=seed; predicate.test(index); index = f.apply(index)) {
* ...
* }
* }</pre>
*
* <p>
* The resulting sequence may be empty if the predicate does not hold on
* the seed value. Otherwise the first element will be the supplied seed
* value, the next element (if present) will be the result of applying the
* function f to the seed value, and so on iteratively until the predicate
* indicates that the stream should terminate.
*
* @param <T> the type of stream elements
* @param seed the initial element
* @param predicate a predicate to apply to elements to determine when the
* stream must terminate.
* @param f a function to be applied to the previous element to produce
* a new element
* @return a new sequential {@code Stream}
* @since 9
*/
public static<T> Stream<T> iterate(T seed, Predicate<? super T> predicate, UnaryOperator<T> f) {
Objects.requireNonNull(f);
Objects.requireNonNull(predicate);
Spliterator<T> spliterator = new Spliterators.AbstractSpliterator<>(Long.MAX_VALUE,
Spliterator.ORDERED | Spliterator.IMMUTABLE) {
T prev;
boolean started, finished;
@Override
public boolean tryAdvance(Consumer<? super T> action) {
Objects.requireNonNull(action);
if (finished)
return false;
T t;
if (started)
t = f.apply(prev);
else {
t = seed;
started = true;
}
if (!predicate.test(t)) {
prev = null;
finished = true;
return false;
}
action.accept(prev = t);
return true;
}
@Override
public T next() {
return t = (t == Streams.NONE) ? seed : f.apply(t);
public void forEachRemaining(Consumer<? super T> action) {
Objects.requireNonNull(action);
if (finished)
return;
finished = true;
T t = started ? f.apply(prev) : seed;
prev = null;
while (predicate.test(t)) {
action.accept(t);
t = f.apply(t);
}
}
};
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
iterator,
Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
return StreamSupport.stream(spliterator, false);
}
/**

View File

@ -48,14 +48,6 @@ final class Streams {
throw new Error("no instances");
}
/**
* An object instance representing no value, that cannot be an actual
* data element of a stream. Used when processing streams that can contain
* {@code null} elements to distinguish between a {@code null} value and no
* value.
*/
static final Object NONE = new Object();
/**
* An {@code int} range spliterator.
*/

View File

@ -126,6 +126,9 @@ public class DoubleStreamTestDataProvider {
() -> Spliterators.spliterator(isl.iterator(), doubles.length, 0)));
spliterators.add(splitDescr("Primitives.s(SpinedBuffer.iterator()):" + name,
() -> Spliterators.spliteratorUnknownSize(isl.iterator(), 0)));
spliterators.add(splitDescr("DoubleStream.iterate(0,x->x<l;x->x+1):" + name,
() -> DoubleStream.iterate(0.0, x -> x < doubles.length, x -> x + 1.0)
.spliterator()));
// Need more!
}
spliteratorTestData = spliterators.toArray(new Object[0][]);

View File

@ -136,6 +136,8 @@ public class IntStreamTestDataProvider {
() -> IntStream.range(0, ints.length).spliterator()));
spliterators.add(splitDescr("IntStream.intRangeClosed(0,l):" + name,
() -> IntStream.rangeClosed(0, ints.length).spliterator()));
spliterators.add(splitDescr("IntStream.iterate(0,x->x<l,x->x+1): " + name,
() -> IntStream.iterate(0, x -> x < ints.length, x -> x + 1).spliterator()));
// Need more!
}
spliteratorTestData = spliterators.toArray(new Object[0][]);

View File

@ -136,6 +136,9 @@ public class LongStreamTestDataProvider {
() -> LongStream.range(0, longs.length).spliterator()));
spliterators.add(splitDescr("LongStream.longRangeClosed(0,l):" + name,
() -> LongStream.rangeClosed(0, longs.length).spliterator()));
spliterators.add(splitDescr("LongStream.iterate(0,x->x<l;x->x+1):" + name,
() -> LongStream.iterate(0L, x -> x < longs.length, x -> x + 1L)
.spliterator()));
// Need more!
}
spliteratorTestData = spliterators.toArray(new Object[0][]);

View File

@ -171,6 +171,8 @@ public class StreamTestDataProvider {
() -> Spliterators.spliterator(Arrays.asList(ints).iterator(), ints.length, 0)));
spliterators.add(splitDescr("Iterators.s(Arrays.s(array).iterator()):" + name,
() -> Spliterators.spliteratorUnknownSize(Arrays.asList(ints).iterator(), 0)));
spliterators.add(splitDescr("Stream.iterate(0,x->x<l,x->x+1): " + name,
() -> Stream.iterate(0, x -> x < ints.length, x -> x + 1).spliterator()));
// @@@ Add map and collection spliterators when spliterator() is exposed on Collection or Iterable
}
spliteratorTestData = spliterators.toArray(new Object[0][]);

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8072727
*/
package org.openjdk.tests.java.util.stream;
import java.util.List;
import java.util.Objects;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.OpTestCase;
import java.util.stream.Stream;
import java.util.stream.TestData;
import java.util.stream.TestData.Factory;
import static java.util.stream.ThowableHelper.checkNPE;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@Test
public class IterateTest extends OpTestCase {
@DataProvider(name = "IterateStreamsData")
public static Object[][] makeIterateStreamsTestData() {
Object[][] data = {
{List.of(),
Factory.ofSupplier("ref.empty", () -> Stream.iterate(1, x -> x < 0, x -> x * 2))},
{List.of(1),
Factory.ofSupplier("ref.one", () -> Stream.iterate(1, x -> x < 2, x -> x * 2))},
{List.of(1, 2, 4, 8, 16, 32, 64, 128, 256, 512),
Factory.ofSupplier("ref.ten", () -> Stream.iterate(1, x -> x < 1000, x -> x * 2))},
{List.of(10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
Factory.ofSupplier("ref.nullCheck", () -> Stream.iterate(10, Objects::nonNull, x -> x > 0 ? x - 1 : null))},
{List.of(),
Factory.ofIntSupplier("int.empty", () -> IntStream.iterate(1, x -> x < 0, x -> x + 1))},
{List.of(1),
Factory.ofIntSupplier("int.one", () -> IntStream.iterate(1, x -> x < 2, x -> x + 1))},
{List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10),
Factory.ofIntSupplier("int.ten", () -> IntStream.iterate(1, x -> x <= 10, x -> x + 1))},
{List.of(5, 4, 3, 2, 1),
Factory.ofIntSupplier("int.divZero", () -> IntStream.iterate(5, x -> x != 0, x -> x - 1/x/2 - 1))},
{List.of(),
Factory.ofLongSupplier("long.empty", () -> LongStream.iterate(1L, x -> x < 0, x -> x + 1))},
{List.of(1L),
Factory.ofLongSupplier("long.one", () -> LongStream.iterate(1L, x -> x < 2, x -> x + 1))},
{List.of(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L),
Factory.ofLongSupplier("long.ten", () -> LongStream.iterate(1L, x -> x <= 10, x -> x + 1))},
{List.of(),
Factory.ofDoubleSupplier("double.empty", () -> DoubleStream.iterate(1.0, x -> x < 0, x -> x + 1))},
{List.of(1.0),
Factory.ofDoubleSupplier("double.one", () -> DoubleStream.iterate(1.0, x -> x < 2, x -> x + 1))},
{List.of(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0),
Factory.ofDoubleSupplier("double.ten", () -> DoubleStream.iterate(1.0, x -> x <= 10, x -> x + 1))}
};
return data;
}
@Test(dataProvider = "IterateStreamsData")
public <T> void testIterate(List<T> expected, TestData<T, ?> data) {
withData(data).stream(s -> s).expectedResult(expected).exercise();
}
@Test
public void testNPE() {
checkNPE(() -> Stream.iterate("", null, x -> x + "a"));
checkNPE(() -> Stream.iterate("", String::isEmpty, null));
checkNPE(() -> IntStream.iterate(0, null, x -> x + 1));
checkNPE(() -> IntStream.iterate(0, x -> x < 10, null));
checkNPE(() -> LongStream.iterate(0, null, x -> x + 1));
checkNPE(() -> LongStream.iterate(0, x -> x < 10, null));
checkNPE(() -> DoubleStream.iterate(0, null, x -> x + 1));
checkNPE(() -> DoubleStream.iterate(0, x -> x < 10, null));
}
}