This commit is contained in:
Alan Bateman 2013-06-11 14:09:06 +01:00
commit 6788137f6f
6 changed files with 407 additions and 251 deletions

View File

@ -759,12 +759,13 @@ public interface IntStream extends BaseStream<Integer, IntStream> {
/** /**
* Returns a sequential {@code IntStream} from {@code startInclusive} * Returns a sequential {@code IntStream} from {@code startInclusive}
* (inclusive) to {@code endExclusive} (exclusive) by an incremental step of * (inclusive) to {@code endExclusive} (exclusive) by an incremental step of
* 1. * {@code 1}.
* *
* @implSpec * @apiNote
* The implementation behaves as if: * <p>An equivalent sequence of increasing values can be produced
* sequentially using a {@code for} loop as follows:
* <pre>{@code * <pre>{@code
* intRange(startInclusive, endExclusive, 1); * for (int i = startInclusive; i < endExclusive ; i++) { ... }
* }</pre> * }</pre>
* *
* @param startInclusive the (inclusive) initial value * @param startInclusive the (inclusive) initial value
@ -773,36 +774,37 @@ public interface IntStream extends BaseStream<Integer, IntStream> {
* elements * elements
*/ */
public static IntStream range(int startInclusive, int endExclusive) { public static IntStream range(int startInclusive, int endExclusive) {
return range(startInclusive, endExclusive, 1); if (startInclusive >= endExclusive) {
return empty();
} else {
return StreamSupport.intStream(
new Streams.RangeIntSpliterator(startInclusive, endExclusive, false));
}
} }
/** /**
* Returns a sequential {@code IntStream} from {@code startInclusive} * Returns a sequential {@code IntStream} from {@code startInclusive}
* (inclusive) to {@code endExclusive} (exclusive) by a positive {@code * (inclusive) to {@code endInclusive} (inclusive) by an incremental step of
* step}. If {@code startInclusive} is greater than or equal to {@code * {@code 1}.
* endExclusive}, an empty stream is returned.
* *
* @apiNote
* <p>An equivalent sequence of increasing values can be produced * <p>An equivalent sequence of increasing values can be produced
* sequentially using a {@code for} loop as follows: * sequentially using a {@code for} loop as follows:
* <pre>{@code * <pre>{@code
* for (int i = startInclusive; i < endExclusive ; i += step) { ... } * for (int i = startInclusive; i <= endInclusive ; i++) { ... }
* }</pre> * }</pre>
* *
* @param startInclusive the (inclusive) initial value * @param startInclusive the (inclusive) initial value
* @param endExclusive the exclusive upper bound * @param endInclusive the inclusive upper bound
* @param step the positive difference between consecutive values
* @return a sequential {@code IntStream} for the range of {@code int} * @return a sequential {@code IntStream} for the range of {@code int}
* elements * elements
* @throws IllegalArgumentException if {@code step} is less than or equal to
* 0
*/ */
public static IntStream range(int startInclusive, int endExclusive, int step) { public static IntStream rangeClosed(int startInclusive, int endInclusive) {
if (step <= 0) { if (startInclusive > endInclusive) {
throw new IllegalArgumentException(String.format("Illegal step: %d", step));
} else if (startInclusive >= endExclusive) {
return empty(); return empty();
} else { } else {
return StreamSupport.intStream(new Streams.RangeIntSpliterator(startInclusive, endExclusive, step)); return StreamSupport.intStream(
new Streams.RangeIntSpliterator(startInclusive, endInclusive, true));
} }
} }
} }

View File

@ -750,12 +750,13 @@ public interface LongStream extends BaseStream<Long, LongStream> {
/** /**
* Returns a sequential {@code LongStream} from {@code startInclusive} * Returns a sequential {@code LongStream} from {@code startInclusive}
* (inclusive) to {@code endExclusive} (exclusive) by an incremental step of * (inclusive) to {@code endExclusive} (exclusive) by an incremental step of
* 1. * {@code 1}.
* *
* @implSpec * @apiNote
* The implementation behaves as if: * <p>An equivalent sequence of increasing values can be produced
* sequentially using a {@code for} loop as follows:
* <pre>{@code * <pre>{@code
* longRange(startInclusive, endExclusive, 1); * for (long i = startInclusive; i < endExclusive ; i++) { ... }
* }</pre> * }</pre>
* *
* @param startInclusive the (inclusive) initial value * @param startInclusive the (inclusive) initial value
@ -764,36 +765,56 @@ public interface LongStream extends BaseStream<Long, LongStream> {
* elements * elements
*/ */
public static LongStream range(long startInclusive, final long endExclusive) { public static LongStream range(long startInclusive, final long endExclusive) {
return range(startInclusive, endExclusive, 1); if (startInclusive >= endExclusive) {
return empty();
} else if (endExclusive - startInclusive < 0) {
// Size of range > Long.MAX_VALUE
// Split the range in two and concatenate
// Note: if the range is [Long.MIN_VALUE, Long.MAX_VALUE) then
// the lower range, [Long.MIN_VALUE, 0) will be further split in two
// long m = startInclusive + Long.divideUnsigned(endExclusive - startInclusive, 2) + 1;
// return Streams.concat(range(startInclusive, m), range(m, endExclusive));
// This is temporary until Streams.concat is supported
throw new UnsupportedOperationException();
} else {
return StreamSupport.longStream(
new Streams.RangeLongSpliterator(startInclusive, endExclusive, false));
}
} }
/** /**
* Returns a sequential {@code LongStream} from {@code startInclusive} * Returns a sequential {@code LongStream} from {@code startInclusive}
* (inclusive) to {@code endExclusive} (exclusive) by {@code step}. If * (inclusive) to {@code endInclusive} (inclusive) by an incremental step of
* {@code startInclusive} is greater than or equal to {@code * {@code 1}.
* endExclusive}, an empty stream is returned.
* *
* @apiNote
* <p>An equivalent sequence of increasing values can be produced * <p>An equivalent sequence of increasing values can be produced
* sequentially using a {@code for} loop as follows: * sequentially using a {@code for} loop as follows:
* <pre>{@code * <pre>{@code
* for (long i = startInclusive; i < endExclusive ; i += step) { ... } * for (long i = startInclusive; i <= endInclusive ; i++) { ... }
* }</pre> * }</pre>
* *
* @param startInclusive the (inclusive) initial value * @param startInclusive the (inclusive) initial value
* @param endExclusive the exclusive upper bound * @param endInclusive the inclusive upper bound
* @param step the difference between consecutive values
* @return a sequential {@code LongStream} for the range of {@code long} * @return a sequential {@code LongStream} for the range of {@code long}
* elements * elements
* @throws IllegalArgumentException if {@code step} is less than or equal to
* 0
*/ */
public static LongStream range(long startInclusive, final long endExclusive, final long step) { public static LongStream rangeClosed(long startInclusive, final long endInclusive) {
if (step <= 0) { if (startInclusive > endInclusive) {
throw new IllegalArgumentException(String.format("Illegal step: %d", step));
} else if (startInclusive >= endExclusive) {
return empty(); return empty();
} else if (endInclusive - startInclusive + 1 <= 0) {
// Size of range > Long.MAX_VALUE
// Split the range in two and concatenate
// Note: if the range is [Long.MIN_VALUE, Long.MAX_VALUE] then
// the lower range, [Long.MIN_VALUE, 0), and upper range,
// [0, Long.MAX_VALUE], will both be further split in two
// long m = startInclusive + Long.divideUnsigned(endInclusive - startInclusive, 2) + 1;
// return Streams.concat(range(startInclusive, m), rangeClosed(m, endInclusive));
// This is temporary until Streams.concat is supported
throw new UnsupportedOperationException();
} else { } else {
return StreamSupport.longStream(new Streams.RangeLongSpliterator(startInclusive, endExclusive, step)); return StreamSupport.longStream(
new Streams.RangeLongSpliterator(startInclusive, endInclusive, true));
} }
} }
} }

View File

@ -25,7 +25,6 @@
package java.util.stream; package java.util.stream;
import java.util.Comparator; import java.util.Comparator;
import java.util.Iterator;
import java.util.Objects; import java.util.Objects;
import java.util.Spliterator; import java.util.Spliterator;
import java.util.Spliterators; import java.util.Spliterators;
@ -62,39 +61,62 @@ class Streams {
* An {@code int} range spliterator. * An {@code int} range spliterator.
*/ */
static final class RangeIntSpliterator implements Spliterator.OfInt { static final class RangeIntSpliterator implements Spliterator.OfInt {
// Can never be greater that upTo, this avoids overflow if upper bound
// is Integer.MAX_VALUE
// All elements are traversed if from == upTo & last == 0
private int from; private int from;
private final int upTo; private final int upTo;
private final int step; // 1 if the range is closed and the last element has not been traversed
// Otherwise, 0 if the range is open, or is a closed range and all
// elements have been traversed
private int last;
RangeIntSpliterator(int from, int upTo, int step) { RangeIntSpliterator(int from, int upTo, boolean closed) {
this(from, upTo, closed ? 1 : 0);
}
private RangeIntSpliterator(int from, int upTo, int last) {
this.from = from; this.from = from;
this.upTo = upTo; this.upTo = upTo;
this.step = step; this.last = last;
} }
@Override @Override
public boolean tryAdvance(IntConsumer consumer) { public boolean tryAdvance(IntConsumer consumer) {
boolean hasNext = from < upTo; final int i = from;
if (hasNext) { if (i < upTo) {
consumer.accept(from); from++;
from += step; consumer.accept(i);
return true;
} }
return hasNext; else if (last > 0) {
last = 0;
consumer.accept(i);
return true;
}
return false;
} }
@Override @Override
public void forEachRemaining(IntConsumer consumer) { public void forEachRemaining(IntConsumer consumer) {
int hUpTo = upTo; int i = from;
int hStep = step; // hoist accesses and checks from loop final int hUpTo = upTo;
for (int i = from; i < hUpTo; i += hStep) int hLast = last;
consumer.accept(i);
from = upTo; from = upTo;
last = 0;
while (i < hUpTo) {
consumer.accept(i++);
}
if (hLast > 0) {
// Last element of closed range
consumer.accept(i);
}
} }
@Override @Override
public long estimateSize() { public long estimateSize() {
int d = upTo - from; // Ensure ranges of size > Integer.MAX_VALUE report the correct size
return (d / step) + ((d % step == 0) ? 0 : 1); return ((long) upTo) - from + last;
} }
@Override @Override
@ -111,57 +133,108 @@ class Streams {
@Override @Override
public Spliterator.OfInt trySplit() { public Spliterator.OfInt trySplit() {
return estimateSize() <= 1 long size = estimateSize();
return size <= 1
? null ? null
: new RangeIntSpliterator(from, from = from + midPoint(), step); // Left split always has a half-open range
: new RangeIntSpliterator(from, from = from + splitPoint(size), 0);
} }
private int midPoint() { /**
// Size is known to be >= 2 * The spliterator size below which the spliterator will be split
int bisection = (upTo - from) / 2; * at the mid-point to produce balanced splits. Above this size the
// If bisection > step then round down to nearest multiple of step * spliterator will be split at a ratio of
// otherwise round up to step * 1:(RIGHT_BALANCED_SPLIT_RATIO - 1)
return bisection > step ? bisection - bisection % step : step; * to produce right-balanced splits.
*
* <p>Such splitting ensures that for very large ranges that the left
* side of the range will more likely be processed at a lower-depth
* than a balanced tree at the expense of a higher-depth for the right
* side of the range.
*
* <p>This is optimized for cases such as IntStream.ints() that is
* implemented as range of 0 to Integer.MAX_VALUE but is likely to be
* augmented with a limit operation that limits the number of elements
* to a count lower than this threshold.
*/
private static final int BALANCED_SPLIT_THRESHOLD = 1 << 24;
/**
* The split ratio of the left and right split when the spliterator
* size is above BALANCED_SPLIT_THRESHOLD.
*/
private static final int RIGHT_BALANCED_SPLIT_RATIO = 1 << 3;
private int splitPoint(long size) {
int d = (size < BALANCED_SPLIT_THRESHOLD) ? 2 : RIGHT_BALANCED_SPLIT_RATIO;
// 2 <= size <= 2^32
return (int) (size / d);
} }
} }
/** /**
* A {@code long} range spliterator. * A {@code long} range spliterator.
*
* This implementation cannot be used for ranges whose size is greater
* than Long.MAX_VALUE
*/ */
static final class RangeLongSpliterator implements Spliterator.OfLong { static final class RangeLongSpliterator implements Spliterator.OfLong {
// Can never be greater that upTo, this avoids overflow if upper bound
// is Long.MAX_VALUE
// All elements are traversed if from == upTo & last == 0
private long from; private long from;
private final long upTo; private final long upTo;
private final long step; // 1 if the range is closed and the last element has not been traversed
// Otherwise, 0 if the range is open, or is a closed range and all
// elements have been traversed
private int last;
RangeLongSpliterator(long from, long upTo, long step) { RangeLongSpliterator(long from, long upTo, boolean closed) {
this(from, upTo, closed ? 1 : 0);
}
private RangeLongSpliterator(long from, long upTo, int last) {
assert upTo - from + last > 0;
this.from = from; this.from = from;
this.upTo = upTo; this.upTo = upTo;
this.step = step; this.last = last;
} }
@Override @Override
public boolean tryAdvance(LongConsumer consumer) { public boolean tryAdvance(LongConsumer consumer) {
boolean hasNext = from < upTo; final long i = from;
if (hasNext) { if (i < upTo) {
consumer.accept(from); from++;
from += step; consumer.accept(i);
return true;
} }
return hasNext; else if (last > 0) {
last = 0;
consumer.accept(i);
return true;
}
return false;
} }
@Override @Override
public void forEachRemaining(LongConsumer consumer) { public void forEachRemaining(LongConsumer consumer) {
long hUpTo = upTo; long i = from;
long hStep = step; // hoist accesses and checks from loop final long hUpTo = upTo;
for (long i = from; i < hUpTo; i += hStep) int hLast = last;
consumer.accept(i);
from = upTo; from = upTo;
last = 0;
while (i < hUpTo) {
consumer.accept(i++);
}
if (hLast > 0) {
// Last element of closed range
consumer.accept(i);
}
} }
@Override @Override
public long estimateSize() { public long estimateSize() {
long d = upTo - from; return upTo - from + last;
return (d / step) + ((d % step == 0) ? 0 : 1);
} }
@Override @Override
@ -178,17 +251,42 @@ class Streams {
@Override @Override
public Spliterator.OfLong trySplit() { public Spliterator.OfLong trySplit() {
return estimateSize() <= 1 long size = estimateSize();
return size <= 1
? null ? null
: new RangeLongSpliterator(from, from = from + midPoint(), step); // Left split always has a half-open range
: new RangeLongSpliterator(from, from = from + splitPoint(size), 0);
} }
private long midPoint() { /**
// Size is known to be >= 2 * The spliterator size below which the spliterator will be split
long bisection = (upTo - from) / 2; * at the mid-point to produce balanced splits. Above this size the
// If bisection > step then round down to nearest multiple of step * spliterator will be split at a ratio of
// otherwise round up to step * 1:(RIGHT_BALANCED_SPLIT_RATIO - 1)
return bisection > step ? bisection - bisection % step : step; * to produce right-balanced splits.
*
* <p>Such splitting ensures that for very large ranges that the left
* side of the range will more likely be processed at a lower-depth
* than a balanced tree at the expense of a higher-depth for the right
* side of the range.
*
* <p>This is optimized for cases such as LongStream.longs() that is
* implemented as range of 0 to Long.MAX_VALUE but is likely to be
* augmented with a limit operation that limits the number of elements
* to a count lower than this threshold.
*/
private static final long BALANCED_SPLIT_THRESHOLD = 1 << 24;
/**
* The split ratio of the left and right split when the spliterator
* size is above BALANCED_SPLIT_THRESHOLD.
*/
private static final long RIGHT_BALANCED_SPLIT_RATIO = 1 << 3;
private long splitPoint(long size) {
long d = (size < BALANCED_SPLIT_THRESHOLD) ? 2 : RIGHT_BALANCED_SPLIT_RATIO;
// 2 <= size <= Long.MAX_VALUE
return size / d;
} }
} }

View File

@ -95,12 +95,8 @@ public class IntStreamTestDataProvider {
list.add(streamDataDescr("IntStream.intRange(0,l): " + ints.length, list.add(streamDataDescr("IntStream.intRange(0,l): " + ints.length,
() -> IntStream.range(0, ints.length))); () -> IntStream.range(0, ints.length)));
list.add(streamDataDescr("IntStream.intRange(0,l,2): " + ints.length, list.add(streamDataDescr("IntStream.rangeClosed(0,l): " + ints.length,
() -> IntStream.range(0, ints.length, 2))); () -> IntStream.rangeClosed(0, ints.length)));
list.add(streamDataDescr("IntStream.intRange(0,l,3): " + ints.length,
() -> IntStream.range(0, ints.length, 3)));
list.add(streamDataDescr("IntStream.intRange(0,l,7): " + ints.length,
() -> IntStream.range(0, ints.length, 7)));
} }
testData = list.toArray(new Object[0][]); testData = list.toArray(new Object[0][]);
} }
@ -131,12 +127,8 @@ public class IntStreamTestDataProvider {
spliterators.add(splitDescr("IntStream.intRange(0,l):" + name, spliterators.add(splitDescr("IntStream.intRange(0,l):" + name,
() -> IntStream.range(0, ints.length).spliterator())); () -> IntStream.range(0, ints.length).spliterator()));
spliterators.add(splitDescr("IntStream.intRange(0,l,2):" + name, spliterators.add(splitDescr("IntStream.intRangeClosed(0,l):" + name,
() -> IntStream.range(0, ints.length, 2).spliterator())); () -> IntStream.rangeClosed(0, ints.length).spliterator()));
spliterators.add(splitDescr("IntStream.intRange(0,l,3):" + name,
() -> IntStream.range(0, ints.length, 3).spliterator()));
spliterators.add(splitDescr("IntStream.intRange(0,l,7):" + name,
() -> IntStream.range(0, ints.length, 7).spliterator()));
// Need more! // Need more!
} }

View File

@ -95,12 +95,8 @@ public class LongStreamTestDataProvider {
list.add(streamDataDescr("LongStream.longRange(0,l): " + longs.length, list.add(streamDataDescr("LongStream.longRange(0,l): " + longs.length,
() -> LongStream.range(0, longs.length))); () -> LongStream.range(0, longs.length)));
list.add(streamDataDescr("LongStream.longRange(0,l,2): " + longs.length, list.add(streamDataDescr("LongStream.longRangeClosed(0,l): " + longs.length,
() -> LongStream.range(0, longs.length, 2))); () -> LongStream.rangeClosed(0, longs.length)));
list.add(streamDataDescr("LongStream.longRange(0,l,3): " + longs.length,
() -> LongStream.range(0, longs.length, 3)));
list.add(streamDataDescr("LongStream.longRange(0,l,7): " + longs.length,
() -> LongStream.range(0, longs.length, 7)));
} }
testData = list.toArray(new Object[0][]); testData = list.toArray(new Object[0][]);
} }
@ -131,12 +127,8 @@ public class LongStreamTestDataProvider {
spliterators.add(splitDescr("LongStream.longRange(0,l):" + name, spliterators.add(splitDescr("LongStream.longRange(0,l):" + name,
() -> LongStream.range(0, longs.length).spliterator())); () -> LongStream.range(0, longs.length).spliterator()));
spliterators.add(splitDescr("LongStream.longRange(0,l,2):" + name, spliterators.add(splitDescr("LongStream.longRangeClosed(0,l):" + name,
() -> LongStream.range(0, longs.length, 2).spliterator())); () -> LongStream.rangeClosed(0, longs.length).spliterator()));
spliterators.add(splitDescr("LongStream.longRange(0,l,3):" + name,
() -> LongStream.range(0, longs.length, 3).spliterator()));
spliterators.add(splitDescr("LongStream.longRange(0,l,7):" + name,
() -> LongStream.range(0, longs.length, 7).spliterator()));
// Need more! // Need more!
} }
spliteratorTestData = spliterators.toArray(new Object[0][]); spliteratorTestData = spliterators.toArray(new Object[0][]);

View File

@ -24,10 +24,11 @@ package org.openjdk.tests.java.util.stream;
import java.util.Arrays; import java.util.Arrays;
import java.util.Optional; import java.util.Optional;
import java.util.stream.DoubleStream; import java.util.Spliterator;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import java.util.stream.LongStream; import java.util.stream.LongStream;
import java.util.stream.OpTestCase; import java.util.stream.OpTestCase;
import java.util.stream.SpliteratorTestHelper;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.stream.TestData; import java.util.stream.TestData;
@ -54,73 +55,76 @@ public class RangeTest extends OpTestCase {
// //
public void testIntRangeErrors() {
for (int start : Arrays.asList(1, 10, -1, -10)) {
for (int end : Arrays.asList(1, 10, -1, -10)) {
for (int step : Arrays.asList(0, 1, -1, Integer.MAX_VALUE, Integer.MIN_VALUE)) {
if (step > 0)
executeAndNoCatch(() -> IntStream.range(start, end, step));
else
executeAndCatch(() -> IntStream.range(start, end, step));
}
}
}
}
public void testIntRange() { public void testIntRange() {
// Without step // Half-open
for (int start : Arrays.asList(1, 10, -1, -10)) { for (int start : Arrays.asList(1, 10, -1, -10)) {
for (int end : Arrays.asList(1, 10, -1, -10)) { for (int end : Arrays.asList(1, 10, -1, -10)) {
int step = 1;
int size = (start < end) ? end - start : 0; int size = (start < end) ? end - start : 0;
int[] exp = new int[size]; int[] exp = new int[size];
if (start < end) { for (int i = start, p = 0; i < end; i++, p++) {
for (int i = start, p = 0; i < end; i++, p++) { exp[p] = i;
exp[p] = i;
}
} }
int[] inc = IntStream.range(start, end).toArray(); int[] inc = IntStream.range(start, end).toArray();
assertEquals(inc.length, size); assertEquals(inc.length, size);
assertTrue(Arrays.equals(exp, inc)); assertTrue(Arrays.equals(exp, inc));
withData(intRangeData(start, end, step)).stream(s -> s). withData(intRangeData(start, end)).stream(s -> s).
expectedResult(exp).exercise(); expectedResult(exp).exercise();
} }
} }
// With step // Closed
for (int start : Arrays.asList(1, 10, -1, -10)) { for (int start : Arrays.asList(1, 10, -1, -10)) {
for (int end : Arrays.asList(1, 10, -1, -10)) { for (int end : Arrays.asList(1, 10, -1, -10)) {
for (int step : Arrays.asList(1, -1, -2, 2)) { int size = (start <= end) ? end - start + 1 : 0;
if (step > 0) { int[] exp = new int[size];
int d = end - start; for (int i = start, p = 0; i <= end; i++, p++) {
int size = (start < end) ? (d / step) + ((d % step == 0) ? 0 : 1) : 0; exp[p] = i;
int[] exp = new int[size];
if (start < end) {
for (int i = start, p = 0; i < end; i += step, p++) {
exp[p] = i;
}
}
int[] inc = IntStream.range(start, end, step).toArray();
assertEquals(inc.length, size);
assertTrue(Arrays.equals(exp, inc));
withData(intRangeData(start, end, step)).stream(s -> s).
expectedResult(exp).exercise();
}
} }
int[] inc = IntStream.rangeClosed(start, end).toArray();
assertEquals(inc.length, size);
assertTrue(Arrays.equals(exp, inc));
withData(intRangeClosedData(start, end)).stream(s -> s).
expectedResult(exp).exercise();
} }
} }
// Closed, maximum upper bound of Integer.MAX_VALUE
{
int[] inc = IntStream.rangeClosed(Integer.MAX_VALUE - 1, Integer.MAX_VALUE).toArray();
assertEquals(2, inc.length);
assertEquals(Integer.MAX_VALUE - 1, inc[0]);
assertEquals(Integer.MAX_VALUE, inc[1]);
inc = IntStream.rangeClosed(Integer.MAX_VALUE, Integer.MAX_VALUE).toArray();
assertEquals(1, inc.length);
assertEquals(Integer.MAX_VALUE, inc[0]);
SpliteratorTestHelper.testIntSpliterator(
() -> IntStream.rangeClosed(Integer.MAX_VALUE - 8, Integer.MAX_VALUE).spliterator());
}
// Range wider than Integer.MAX_VALUE
{
Spliterator.OfInt s = IntStream.rangeClosed(Integer.MIN_VALUE, Integer.MAX_VALUE).
spliterator();
assertEquals(s.estimateSize(), 1L << 32);
}
} }
TestData.OfInt intRangeData(int start, int end, int step) { TestData.OfInt intRangeData(int start, int end) {
return TestData.Factory.ofIntSupplier("int range", () -> IntStream.range(start, end, step)); return TestData.Factory.ofIntSupplier("int range", () -> IntStream.range(start, end));
}
TestData.OfInt intRangeClosedData(int start, int end) {
return TestData.Factory.ofIntSupplier("int rangeClosed", () -> IntStream.rangeClosed(start, end));
} }
public void tesIntRangeReduce() { public void tesIntRangeReduce() {
withData(intRangeData(0, 10000, 1)). withData(intRangeData(0, 10000)).
terminal(s -> s.reduce(0, Integer::sum)).exercise(); terminal(s -> s.reduce(0, Integer::sum)).exercise();
} }
@ -137,74 +141,69 @@ public class RangeTest extends OpTestCase {
// //
public void testLongRangeErrors() {
for (long start : Arrays.asList(1, 10, -1, -10)) {
for (long end : Arrays.asList(1, 10, -1, -10)) {
for (long step : Arrays.asList(0L, 1L, -1L, Long.MAX_VALUE, Long.MIN_VALUE)) {
if (step > 0)
executeAndNoCatch(() -> LongStream.range(start, end, step));
else
executeAndCatch(() -> LongStream.range(start, end, step));
}
}
}
}
public void testLongRange() { public void testLongRange() {
// Without step // Half-open
for (long start : Arrays.asList(1, 1000, -1, -1000)) { for (long start : Arrays.asList(1, 1000, -1, -1000)) {
for (long end : Arrays.asList(1, 1000, -1, -1000)) { for (long end : Arrays.asList(1, 1000, -1, -1000)) {
long step = 1;
long size = start < end ? end - start : 0; long size = start < end ? end - start : 0;
long[] exp = new long[(int) size]; long[] exp = new long[(int) size];
if (start < end) { for (long i = start, p = 0; i < end; i++, p++) {
for (long i = start, p = 0; i < end; i++, p++) { exp[(int) p] = i;
exp[(int) p] = i;
}
} }
long[] inc = LongStream.range(start, end).toArray(); long[] inc = LongStream.range(start, end).toArray();
assertEquals(inc.length, size); assertEquals(inc.length, size);
assertTrue(Arrays.equals(exp, inc)); assertTrue(Arrays.equals(exp, inc));
withData(longRangeData(start, end, step)).stream(s -> s). withData(longRangeData(start, end)).stream(s -> s).
expectedResult(exp).exercise(); expectedResult(exp).exercise();
} }
} }
// With step // Closed
for (long start : Arrays.asList(1, 1000, -1, -1000)) { for (long start : Arrays.asList(1, 1000, -1, -1000)) {
for (long end : Arrays.asList(1, 1000, -1, -1000)) { for (long end : Arrays.asList(1, 1000, -1, -1000)) {
for (long step : Arrays.asList(1, -1, -2, 2)) { long size = start <= end ? end - start + 1: 0;
if (step > 0) { long[] exp = new long[(int) size];
for (long i = start, p = 0; i <= end; i++, p++) {
long d = end - start; exp[(int) p] = i;
long size = start < end ? (d / step) + ((d % step == 0) ? 0 : 1) : 0;
long[] exp = new long[(int) size];
if (start < end) {
for (long i = start, p = 0; i < end; i += step, p++) {
exp[(int) p] = i;
}
}
long[] inc = LongStream.range(start, end, step).toArray();
assertEquals(inc.length, size);
assertTrue(Arrays.equals(exp, inc));
withData(longRangeData(start, end, step)).stream(s -> s).
expectedResult(exp).exercise();
}
} }
long[] inc = LongStream.rangeClosed(start, end).toArray();
assertEquals(inc.length, size);
assertTrue(Arrays.equals(exp, inc));
withData(longRangeClosedData(start, end)).stream(s -> s).
expectedResult(exp).exercise();
} }
} }
// Closed, maximum upper bound of Long.MAX_VALUE
{
long[] inc = LongStream.rangeClosed(Long.MAX_VALUE - 1, Long.MAX_VALUE).toArray();
assertEquals(2, inc.length);
assertEquals(Long.MAX_VALUE - 1, inc[0]);
assertEquals(Long.MAX_VALUE, inc[1]);
inc = LongStream.rangeClosed(Long.MAX_VALUE, Long.MAX_VALUE).toArray();
assertEquals(1, inc.length);
assertEquals(Long.MAX_VALUE, inc[0]);
SpliteratorTestHelper.testLongSpliterator(
() -> LongStream.rangeClosed(Long.MAX_VALUE - 8, Long.MAX_VALUE).spliterator());
}
} }
TestData.OfLong longRangeData(long start, long end, long step) { TestData.OfLong longRangeData(long start, long end) {
return TestData.Factory.ofLongSupplier("long range", () -> LongStream.range(start, end, step)); return TestData.Factory.ofLongSupplier("long range", () -> LongStream.range(start, end));
}
TestData.OfLong longRangeClosedData(long start, long end) {
return TestData.Factory.ofLongSupplier("long rangeClosed", () -> LongStream.rangeClosed(start, end));
} }
public void testLongRangeReduce() { public void testLongRangeReduce() {
withData(longRangeData(0, 10000, 1)). withData(longRangeData(0, 10000)).
terminal(s -> s.reduce(0, Long::sum)).exercise(); terminal(s -> s.reduce(0, Long::sum)).exercise();
} }
@ -219,64 +218,116 @@ public class RangeTest extends OpTestCase {
assertEquals(first, LongStream.iterate(0, i -> i + 1).parallel().filter(i -> i > 10000).findFirst().getAsLong()); assertEquals(first, LongStream.iterate(0, i -> i + 1).parallel().filter(i -> i > 10000).findFirst().getAsLong());
} }
// // Enable when Stream.concat is present and range implementations are
// updated to use that
private static int[] reverse(int[] a) { // private static void assertSizedAndSubSized(Spliterator<?> s) {
int[] b = new int[a.length]; // assertTrue(s.hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED));
for (int i = 0; i < a.length; i++) { // }
b[b.length - i - 1] = a[i]; //
} // private static void assertNotSizedAndSubSized(Spliterator<?> s) {
return b; // assertFalse(s.hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED));
} // }
//
private static long[] reverse(long[] a) { // public void testLongLongRange() {
long[] b = new long[a.length]; // // Test [Long.MIN_VALUE, Long.MAX_VALUE)
for (int i = 0; i < a.length; i++) { // // This will concatenate streams of three ranges
b[b.length - i - 1] = a[i]; // // [Long.MIN_VALUE, x) [x, 0) [0, Long.MAX_VALUE)
} // // where x = Long.divideUnsigned(0 - Long.MIN_VALUE, 2) + 1
return b; // {
} // Spliterator.OfLong s = LongStream.range(Long.MIN_VALUE, Long.MAX_VALUE).spliterator();
//
private static double[] reverse(double[] a) { // assertEquals(s.estimateSize(), Long.MAX_VALUE);
double[] b = new double[a.length]; // assertNotSizedAndSubSized(s);
for (int i = 0; i < a.length; i++) { //
b[b.length - i - 1] = a[i]; // Spliterator.OfLong s1 = s.trySplit();
} // assertNotSizedAndSubSized(s1);
return b; // assertSizedAndSubSized(s);
} //
// Spliterator.OfLong s2 = s1.trySplit();
private void executeAndCatch(Runnable r) { // assertSizedAndSubSized(s1);
executeAndCatch(IllegalArgumentException.class, r); // assertSizedAndSubSized(s2);
} //
// assertTrue(s.estimateSize() == Long.MAX_VALUE);
private void executeAndNoCatch(Runnable r) { // assertTrue(s1.estimateSize() < Long.MAX_VALUE);
executeAndCatch(null, r); // assertTrue(s2.estimateSize() < Long.MAX_VALUE);
} //
// assertEquals(s.estimateSize() + s1.estimateSize() + s2.estimateSize(),
private void executeAndCatch(Class<? extends Exception> expected, Runnable r) { // Long.MAX_VALUE - Long.MIN_VALUE);
Exception caught = null; // }
try { //
r.run(); // long[][] ranges = { {Long.MIN_VALUE, 0}, {-1, Long.MAX_VALUE} };
} // for (int i = 0; i < ranges.length; i++) {
catch (Exception e) { // long start = ranges[i][0];
caught = e; // long end = ranges[i][1];
} //
// Spliterator.OfLong s = LongStream.range(start, end).spliterator();
if (expected != null) { //
assertNotNull(caught, // assertEquals(s.estimateSize(), Long.MAX_VALUE);
String.format("No Exception was thrown, expected an Exception of %s to be thrown", // assertNotSizedAndSubSized(s);
expected.getName())); //
assertTrue(expected.isInstance(caught), // Spliterator.OfLong s1 = s.trySplit();
String.format("Exception thrown %s not an instance of %s", // assertSizedAndSubSized(s1);
caught.getClass().getName(), expected.getName())); // assertSizedAndSubSized(s);
} //
else { // assertTrue(s.estimateSize() < Long.MAX_VALUE);
if (caught != null) { // assertTrue(s1.estimateSize() < Long.MAX_VALUE);
assertNull(caught, //
String.format("Unexpected exception of %s was thrown", // assertEquals(s.estimateSize() + s1.estimateSize(), end - start);
caught.getClass().getName())); // }
} // }
} //
} // public void testLongLongRangeClosed() {
// // Test [Long.MIN_VALUE, Long.MAX_VALUE]
// // This will concatenate streams of four ranges
// // [Long.MIN_VALUE, x) [x, 0) [0, y) [y, Long.MAX_VALUE]
// // where x = Long.divideUnsigned(0 - Long.MIN_VALUE, 2) + 1
// // y = Long.divideUnsigned(Long.MAX_VALUE, 2) + 1
//
// {
// Spliterator.OfLong s = LongStream.rangeClosed(Long.MIN_VALUE, Long.MAX_VALUE).spliterator();
//
// assertEquals(s.estimateSize(), Long.MAX_VALUE);
// assertNotSizedAndSubSized(s);
//
// Spliterator.OfLong s1 = s.trySplit();
// assertNotSizedAndSubSized(s1);
// assertNotSizedAndSubSized(s);
//
// Spliterator.OfLong s2 = s1.trySplit();
// assertSizedAndSubSized(s1);
// assertSizedAndSubSized(s2);
//
// Spliterator.OfLong s3 = s.trySplit();
// assertSizedAndSubSized(s3);
// assertSizedAndSubSized(s);
//
// assertTrue(s.estimateSize() < Long.MAX_VALUE);
// assertTrue(s3.estimateSize() < Long.MAX_VALUE);
// assertTrue(s1.estimateSize() < Long.MAX_VALUE);
// assertTrue(s2.estimateSize() < Long.MAX_VALUE);
//
// assertEquals(s.estimateSize() + s3.estimateSize() + s1.estimateSize() + s2.estimateSize(),
// Long.MAX_VALUE - Long.MIN_VALUE + 1);
// }
//
// long[][] ranges = { {Long.MIN_VALUE, 0}, {-1, Long.MAX_VALUE} };
// for (int i = 0; i < ranges.length; i++) {
// long start = ranges[i][0];
// long end = ranges[i][1];
//
// Spliterator.OfLong s = LongStream.rangeClosed(start, end).spliterator();
//
// assertEquals(s.estimateSize(), Long.MAX_VALUE);
// assertNotSizedAndSubSized(s);
//
// Spliterator.OfLong s1 = s.trySplit();
// assertSizedAndSubSized(s1);
// assertSizedAndSubSized(s);
//
// assertTrue(s.estimateSize() < Long.MAX_VALUE);
// assertTrue(s1.estimateSize() < Long.MAX_VALUE);
//
// assertEquals(s.estimateSize() + s1.estimateSize(), end - start + 1);
// }
// }
} }