8016308: Updates to j.u.stream.Node/Nodes

Co-authored-by: Brian Goetz <brian.goetz@oracle.com>
Reviewed-by: mduigou
This commit is contained in:
Paul Sandoz 2013-06-20 10:45:46 +02:00
parent 1f4dfcf422
commit 434bea45ac
6 changed files with 550 additions and 848 deletions

View File

@ -104,6 +104,32 @@ interface Node<T> {
throw new IndexOutOfBoundsException();
}
/**
* Return a node describing a subsequence of the elements of this node,
* starting at the given inclusive start offset and ending at the given
* exclusive end offset.
*
* @param from The (inclusive) starting offset of elements to include, must
* be in range 0..count().
* @param to The (exclusive) end offset of elements to include, must be
* in range 0..count().
* @param generator A function to be used to create a new array, if needed,
* for reference nodes.
* @return the truncated node
*/
default Node<T> truncate(long from, long to, IntFunction<T[]> generator) {
if (from == 0 && to == count())
return this;
Spliterator<T> spliterator = spliterator();
long size = to - from;
Node.Builder<T> nodeBuilder = Nodes.builder(size, generator);
nodeBuilder.begin(size);
for (int i = 0; i < from && spliterator.tryAdvance(e -> { }); i++) { }
for (int i = 0; (i < size) && spliterator.tryAdvance(nodeBuilder); i++) { }
nodeBuilder.end();
return nodeBuilder.build();
}
/**
* Provides an array view of the contents of this node.
*
@ -192,19 +218,90 @@ interface Node<T> {
}
}
/**
* Specialized {@code Node} for int elements
*/
interface OfInt extends Node<Integer> {
public interface OfPrimitive<T, T_CONS, T_ARR,
T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>,
T_NODE extends OfPrimitive<T, T_CONS, T_ARR, T_SPLITR, T_NODE>>
extends Node<T> {
/**
* {@inheritDoc}
*
* @return a {@link Spliterator.OfInt} describing the elements of this
* node
* @return a {@link Spliterator.OfPrimitive} describing the elements of
* this node
*/
@Override
Spliterator.OfInt spliterator();
T_SPLITR spliterator();
/**
* Traverses the elements of this node, and invoke the provided
* {@code action} with each element.
*
* @param action a consumer that is to be invoked with each
* element in this {@code Node.OfPrimitive}
*/
void forEach(T_CONS action);
@Override
default T_NODE getChild(int i) {
throw new IndexOutOfBoundsException();
}
T_NODE truncate(long from, long to, IntFunction<T[]> generator);
/**
* {@inheritDoc}
*
* @implSpec the default implementation invokes the generator to create
* an instance of a boxed primitive array with a length of
* {@link #count()} and then invokes {@link #copyInto(T[], int)} with
* that array at an offset of 0.
*/
@Override
default T[] asArray(IntFunction<T[]> generator) {
T[] boxed = generator.apply((int) count());
copyInto(boxed, 0);
return boxed;
}
/**
* Views this node as a primitive array.
*
* <p>Depending on the underlying implementation this may return a
* reference to an internal array rather than a copy. It is the callers
* responsibility to decide if either this node or the array is utilized
* as the primary reference for the data.</p>
*
* @return an array containing the contents of this {@code Node}
*/
T_ARR asPrimitiveArray();
/**
* Creates a new primitive array.
*
* @param count the length of the primitive array.
* @return the new primitive array.
*/
T_ARR newArray(int count);
/**
* Copies the content of this {@code Node} into a primitive array,
* starting at a given offset into the array. It is the caller's
* responsibility to ensure there is sufficient room in the array.
*
* @param array the array into which to copy the contents of this
* {@code Node}
* @param offset the starting offset within the array
* @throws IndexOutOfBoundsException if copying would cause access of
* data outside array bounds
* @throws NullPointerException if {@code array} is {@code null}
*/
void copyInto(T_ARR array, int offset);
}
/**
* Specialized {@code Node} for int elements
*/
interface OfInt extends OfPrimitive<Integer, IntConsumer, int[], Spliterator.OfInt, OfInt> {
/**
* {@inheritDoc}
@ -226,38 +323,13 @@ interface Node<T> {
}
}
/**
* Traverses the elements of this node, and invoke the provided
* {@code IntConsumer} with each element.
*
* @param consumer a {@code IntConsumer} that is to be invoked with each
* element in this {@code Node}
*/
void forEach(IntConsumer consumer);
/**
* {@inheritDoc}
*
* @implSpec the default implementation invokes the generator to create
* an instance of an Integer[] array with a length of {@link #count()}
* and then invokes {@link #copyInto(Integer[], int)} with that
* Integer[] array at an offset of 0. This is not efficient and it is
* recommended to invoke {@link #asPrimitiveArray()}.
*/
@Override
default Integer[] asArray(IntFunction<Integer[]> generator) {
Integer[] boxed = generator.apply((int) count());
copyInto(boxed, 0);
return boxed;
}
/**
* {@inheritDoc}
*
* @implSpec the default implementation invokes {@link #asPrimitiveArray()} to
* obtain an int[] array then and copies the elements from that int[]
* array into the boxed Integer[] array. This is not efficient and it
* is recommended to invoke {@link #copyInto(int[], int)}.
* is recommended to invoke {@link #copyInto(Object, int)}.
*/
@Override
default void copyInto(Integer[] boxed, int offset) {
@ -271,35 +343,23 @@ interface Node<T> {
}
@Override
default Node.OfInt getChild(int i) {
throw new IndexOutOfBoundsException();
default Node.OfInt truncate(long from, long to, IntFunction<Integer[]> generator) {
if (from == 0 && to == count())
return this;
long size = to - from;
Spliterator.OfInt spliterator = spliterator();
Node.Builder.OfInt nodeBuilder = Nodes.intBuilder(size);
nodeBuilder.begin(size);
for (int i = 0; i < from && spliterator.tryAdvance((IntConsumer) e -> { }); i++) { }
for (int i = 0; (i < size) && spliterator.tryAdvance((IntConsumer) nodeBuilder); i++) { }
nodeBuilder.end();
return nodeBuilder.build();
}
/**
* Views this node as an int[] array.
*
* <p>Depending on the underlying implementation this may return a
* reference to an internal array rather than a copy. It is the callers
* responsibility to decide if either this node or the array is utilized
* as the primary reference for the data.</p>
*
* @return an array containing the contents of this {@code Node}
*/
int[] asPrimitiveArray();
/**
* Copies the content of this {@code Node} into an int[] array, starting
* at a given offset into the array. It is the caller's responsibility
* to ensure there is sufficient room in the array.
*
* @param array the array into which to copy the contents of this
* {@code Node}
* @param offset the starting offset within the array
* @throws IndexOutOfBoundsException if copying would cause access of
* data outside array bounds
* @throws NullPointerException if {@code array} is {@code null}
*/
void copyInto(int[] array, int offset);
@Override
default int[] newArray(int count) {
return new int[count];
}
/**
* {@inheritDoc}
@ -309,22 +369,12 @@ interface Node<T> {
default StreamShape getShape() {
return StreamShape.INT_VALUE;
}
}
/**
* Specialized {@code Node} for long elements
*/
interface OfLong extends Node<Long> {
/**
* {@inheritDoc}
*
* @return a {@link Spliterator.OfLong} describing the elements of this
* node
*/
@Override
Spliterator.OfLong spliterator();
interface OfLong extends OfPrimitive<Long, LongConsumer, long[], Spliterator.OfLong, OfLong> {
/**
* {@inheritDoc}
@ -346,38 +396,13 @@ interface Node<T> {
}
}
/**
* Traverses the elements of this node, and invoke the provided
* {@code LongConsumer} with each element.
*
* @param consumer a {@code LongConsumer} that is to be invoked with
* each element in this {@code Node}
*/
void forEach(LongConsumer consumer);
/**
* {@inheritDoc}
*
* @implSpec the default implementation invokes the generator to create
* an instance of a Long[] array with a length of {@link #count()} and
* then invokes {@link #copyInto(Long[], int)} with that Long[] array at
* an offset of 0. This is not efficient and it is recommended to
* invoke {@link #asPrimitiveArray()}.
*/
@Override
default Long[] asArray(IntFunction<Long[]> generator) {
Long[] boxed = generator.apply((int) count());
copyInto(boxed, 0);
return boxed;
}
/**
* {@inheritDoc}
*
* @implSpec the default implementation invokes {@link #asPrimitiveArray()}
* to obtain a long[] array then and copies the elements from that
* long[] array into the boxed Long[] array. This is not efficient and
* it is recommended to invoke {@link #copyInto(long[], int)}.
* it is recommended to invoke {@link #copyInto(Object, int)}.
*/
@Override
default void copyInto(Long[] boxed, int offset) {
@ -391,35 +416,23 @@ interface Node<T> {
}
@Override
default Node.OfLong getChild(int i) {
throw new IndexOutOfBoundsException();
default Node.OfLong truncate(long from, long to, IntFunction<Long[]> generator) {
if (from == 0 && to == count())
return this;
long size = to - from;
Spliterator.OfLong spliterator = spliterator();
Node.Builder.OfLong nodeBuilder = Nodes.longBuilder(size);
nodeBuilder.begin(size);
for (int i = 0; i < from && spliterator.tryAdvance((LongConsumer) e -> { }); i++) { }
for (int i = 0; (i < size) && spliterator.tryAdvance((LongConsumer) nodeBuilder); i++) { }
nodeBuilder.end();
return nodeBuilder.build();
}
/**
* Views this node as a long[] array.
*
* <p/>Depending on the underlying implementation this may return a
* reference to an internal array rather than a copy. It is the callers
* responsibility to decide if either this node or the array is utilized
* as the primary reference for the data.
*
* @return an array containing the contents of this {@code Node}
*/
long[] asPrimitiveArray();
/**
* Copies the content of this {@code Node} into a long[] array, starting
* at a given offset into the array. It is the caller's responsibility
* to ensure there is sufficient room in the array.
*
* @param array the array into which to copy the contents of this
* {@code Node}
* @param offset the starting offset within the array
* @throws IndexOutOfBoundsException if copying would cause access of
* data outside array bounds
* @throws NullPointerException if {@code array} is {@code null}
*/
void copyInto(long[] array, int offset);
@Override
default long[] newArray(int count) {
return new long[count];
}
/**
* {@inheritDoc}
@ -429,23 +442,12 @@ interface Node<T> {
default StreamShape getShape() {
return StreamShape.LONG_VALUE;
}
}
/**
* Specialized {@code Node} for double elements
*/
interface OfDouble extends Node<Double> {
/**
* {@inheritDoc}
*
* @return A {@link Spliterator.OfDouble} describing the elements of
* this node
*/
@Override
Spliterator.OfDouble spliterator();
interface OfDouble extends OfPrimitive<Double, DoubleConsumer, double[], Spliterator.OfDouble, OfDouble> {
/**
* {@inheritDoc}
@ -467,40 +469,15 @@ interface Node<T> {
}
}
/**
* Traverses the elements of this node, and invoke the provided
* {@code DoubleConsumer} with each element.
*
* @param consumer A {@code DoubleConsumer} that is to be invoked with
* each element in this {@code Node}
*/
void forEach(DoubleConsumer consumer);
//
/**
* {@inheritDoc}
*
* @implSpec the default implementation invokes the generator to create
* an instance of a Double[] array with a length of {@link #count()} and
* then invokes {@link #copyInto(Double[], int)} with that Double[]
* array at an offset of 0. This is not efficient and it is recommended
* to invoke {@link #asPrimitiveArray()}.
*/
@Override
default Double[] asArray(IntFunction<Double[]> generator) {
Double[] boxed = generator.apply((int) count());
copyInto(boxed, 0);
return boxed;
}
/**
* {@inheritDoc}
*
* @implSpec the default implementation invokes {@link #asPrimitiveArray()}
* to obtain a double[] array then and copies the elements from that
* double[] array into the boxed Double[] array. This is not efficient
* and it is recommended to invoke {@link #copyInto(double[], int)}.
* and it is recommended to invoke {@link #copyInto(Object, int)}.
*/
@Override
default void copyInto(Double[] boxed, int offset) {
@ -514,35 +491,23 @@ interface Node<T> {
}
@Override
default Node.OfDouble getChild(int i) {
throw new IndexOutOfBoundsException();
default Node.OfDouble truncate(long from, long to, IntFunction<Double[]> generator) {
if (from == 0 && to == count())
return this;
long size = to - from;
Spliterator.OfDouble spliterator = spliterator();
Node.Builder.OfDouble nodeBuilder = Nodes.doubleBuilder(size);
nodeBuilder.begin(size);
for (int i = 0; i < from && spliterator.tryAdvance((DoubleConsumer) e -> { }); i++) { }
for (int i = 0; (i < size) && spliterator.tryAdvance((DoubleConsumer) nodeBuilder); i++) { }
nodeBuilder.end();
return nodeBuilder.build();
}
/**
* Views this node as a double[] array.
*
* <p/>Depending on the underlying implementation this may return a
* reference to an internal array rather than a copy. It is the callers
* responsibility to decide if either this node or the array is utilized
* as the primary reference for the data.
*
* @return an array containing the contents of this {@code Node}
*/
double[] asPrimitiveArray();
/**
* Copies the content of this {@code Node} into a double[] array, starting
* at a given offset into the array. It is the caller's responsibility
* to ensure there is sufficient room in the array.
*
* @param array the array into which to copy the contents of this
* {@code Node}
* @param offset the starting offset within the array
* @throws IndexOutOfBoundsException if copying would cause access of
* data outside array bounds
* @throws NullPointerException if {@code array} is {@code null}
*/
void copyInto(double[] array, int offset);
@Override
default double[] newArray(int count) {
return new double[count];
}
/**
* {@inheritDoc}

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,10 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Spliterator;
import java.util.concurrent.CountedCompleter;
import java.util.function.DoubleConsumer;
import java.util.function.IntConsumer;
import java.util.function.IntFunction;
import java.util.function.LongConsumer;
/**
* Factory for instances of a short-circuiting stateful intermediate operations
@ -352,7 +355,7 @@ final class SliceOps {
else
// This will create a tree of depth 1 and will not be a sub-tree
// for leaf nodes within the require range
result = Nodes.conc(op.getOutputShape(), nodes);
result = conc(op.getOutputShape(), nodes);
setLocalResult(result);
}
}
@ -418,94 +421,116 @@ final class SliceOps {
if (skipLeft == 0 && skipRight == 0)
return input;
else {
return Nodes.truncateNode(input, skipLeft, thisNodeSize - skipRight, generator);
return truncateNode(input, skipLeft, thisNodeSize - skipRight, generator);
}
}
/**
* Truncate a {@link Node}, returning a node describing a subsequence of
* the contents of the input node.
*
* @param <T> the type of elements of the input node and truncated node
* @param input the input node
* @param from the starting offset to include in the truncated node (inclusive)
* @param to the ending offset ot include in the truncated node (exclusive)
* @param generator the array factory (only used for reference nodes)
* @return the truncated node
*/
@SuppressWarnings("unchecked")
private static <T> Node<T> truncateNode(Node<T> input, long from, long to, IntFunction<T[]> generator) {
StreamShape shape = input.getShape();
long size = truncatedSize(input.count(), from, to);
if (size == 0)
return Nodes.emptyNode(shape);
else if (from == 0 && to >= input.count())
return input;
switch (shape) {
case REFERENCE: {
Spliterator<T> spliterator = input.spliterator();
Node.Builder<T> nodeBuilder = Nodes.builder(size, generator);
nodeBuilder.begin(size);
for (int i = 0; i < from && spliterator.tryAdvance(e -> { }); i++) { }
for (int i = 0; (i < size) && spliterator.tryAdvance(nodeBuilder); i++) { }
nodeBuilder.end();
return nodeBuilder.build();
}
case INT_VALUE: {
Spliterator.OfInt spliterator = ((Node.OfInt) input).spliterator();
Node.Builder.OfInt nodeBuilder = Nodes.intBuilder(size);
nodeBuilder.begin(size);
for (int i = 0; i < from && spliterator.tryAdvance((IntConsumer) e -> { }); i++) { }
for (int i = 0; (i < size) && spliterator.tryAdvance((IntConsumer) nodeBuilder); i++) { }
nodeBuilder.end();
return (Node<T>) nodeBuilder.build();
}
case LONG_VALUE: {
Spliterator.OfLong spliterator = ((Node.OfLong) input).spliterator();
Node.Builder.OfLong nodeBuilder = Nodes.longBuilder(size);
nodeBuilder.begin(size);
for (int i = 0; i < from && spliterator.tryAdvance((LongConsumer) e -> { }); i++) { }
for (int i = 0; (i < size) && spliterator.tryAdvance((LongConsumer) nodeBuilder); i++) { }
nodeBuilder.end();
return (Node<T>) nodeBuilder.build();
}
case DOUBLE_VALUE: {
Spliterator.OfDouble spliterator = ((Node.OfDouble) input).spliterator();
Node.Builder.OfDouble nodeBuilder = Nodes.doubleBuilder(size);
nodeBuilder.begin(size);
for (int i = 0; i < from && spliterator.tryAdvance((DoubleConsumer) e -> { }); i++) { }
for (int i = 0; (i < size) && spliterator.tryAdvance((DoubleConsumer) nodeBuilder); i++) { }
nodeBuilder.end();
return (Node<T>) nodeBuilder.build();
}
default:
throw new IllegalStateException("Unknown shape " + shape);
}
}
private static long truncatedSize(long size, long from, long to) {
if (from >= 0)
size = Math.max(0, size - from);
long limit = to - from;
if (limit >= 0)
size = Math.min(size, limit);
return size;
}
/**
* Produces a concatenated {@link Node} that has two or more children.
* <p>The count of the concatenated node is equal to the sum of the count
* of each child. Traversal of the concatenated node traverses the content
* of each child in encounter order of the list of children. Splitting a
* spliterator obtained from the concatenated node preserves the encounter
* order of the list of children.
*
* <p>The result may be a concatenated node, the input sole node if the size
* of the list is 1, or an empty node.
*
* @param <T> the type of elements of the concatenated node
* @param shape the shape of the concatenated node to be created
* @param nodes the input nodes
* @return a {@code Node} covering the elements of the input nodes
* @throws IllegalStateException if all {@link Node} elements of the list
* are an not instance of type supported by this factory.
*/
@SuppressWarnings("unchecked")
private static <T> Node<T> conc(StreamShape shape, List<? extends Node<T>> nodes) {
int size = nodes.size();
if (size == 0)
return Nodes.emptyNode(shape);
else if (size == 1)
return nodes.get(0);
else {
// Create a right-balanced tree when there are more that 2 nodes
List<Node<T>> refNodes = (List<Node<T>>) nodes;
Node<T> c = Nodes.conc(shape, refNodes.get(size - 2), refNodes.get(size - 1));
for (int i = size - 3; i >= 0; i--) {
c = Nodes.conc(shape, refNodes.get(i), c);
}
return c;
}
}
}
// @@@ Currently unused -- optimization for when all sizes are known
// private static class SizedSliceTask<S, T> extends AbstractShortCircuitTask<S, T, Node<T>, SizedSliceTask<S, T>> {
// private final int targetOffset, targetSize;
// private final int offset, size;
//
// private SizedSliceTask(ParallelPipelineHelper<S, T> helper, int offset, int size) {
// super(helper);
// targetOffset = offset;
// targetSize = size;
// this.offset = 0;
// this.size = spliterator.getSizeIfKnown();
// }
//
// private SizedSliceTask(SizedSliceTask<S, T> parent, Spliterator<S> spliterator) {
// // Makes assumptions about order in which siblings are created and linked into parent!
// super(parent, spliterator);
// targetOffset = parent.targetOffset;
// targetSize = parent.targetSize;
// int siblingSizes = 0;
// for (SizedSliceTask<S, T> sibling = parent.children; sibling != null; sibling = sibling.nextSibling)
// siblingSizes += sibling.size;
// size = spliterator.getSizeIfKnown();
// offset = parent.offset + siblingSizes;
// }
//
// @Override
// protected SizedSliceTask<S, T> makeChild(Spliterator<S> spliterator) {
// return new SizedSliceTask<>(this, spliterator);
// }
//
// @Override
// protected Node<T> getEmptyResult() {
// return Nodes.emptyNode();
// }
//
// @Override
// public boolean taskCanceled() {
// if (offset > targetOffset+targetSize || offset+size < targetOffset)
// return true;
// else
// return super.taskCanceled();
// }
//
// @Override
// protected Node<T> doLeaf() {
// int skipLeft = Math.max(0, targetOffset - offset);
// int skipRight = Math.max(0, offset + size - (targetOffset + targetSize));
// if (skipLeft == 0 && skipRight == 0)
// return helper.into(Nodes.<T>makeBuilder(spliterator.getSizeIfKnown())).build();
// else {
// // If we're the first or last node that intersects the target range, peel off irrelevant elements
// int truncatedSize = size - skipLeft - skipRight;
// NodeBuilder<T> builder = Nodes.<T>makeBuilder(truncatedSize);
// Sink<S> wrappedSink = helper.wrapSink(builder);
// wrappedSink.begin(truncatedSize);
// Iterator<S> iterator = spliterator.iterator();
// for (int i=0; i<skipLeft; i++)
// iterator.next();
// for (int i=0; i<truncatedSize; i++)
// wrappedSink.apply(iterator.next());
// wrappedSink.end();
// return builder.build();
// }
// }
//
// @Override
// public void onCompletion(CountedCompleter<?> caller) {
// if (!isLeaf()) {
// Node<T> result = null;
// for (SizedSliceTask<S, T> child = children.nextSibling; child != null; child = child.nextSibling) {
// Node<T> childResult = child.getRawResult();
// if (childResult == null)
// continue;
// else if (result == null)
// result = childResult;
// else
// result = Nodes.node(result, childResult);
// }
// setRawResult(result);
// if (offset <= targetOffset && offset+size >= targetOffset+targetSize)
// shortCircuit(result);
// }
// }
// }
}

View File

@ -102,7 +102,7 @@ public class DoubleNodeTest extends OpTestCase {
double i = it.nextDouble();
if (it.hasNext()) {
return new Nodes.DoubleConcNode(Nodes.node(new double[] {i}), degenerateTree(it));
return new Nodes.ConcNode.OfDouble(Nodes.node(new double[] {i}), degenerateTree(it));
}
else {
return Nodes.node(new double[] {i});
@ -114,7 +114,7 @@ public class DoubleNodeTest extends OpTestCase {
return m.apply(l);
}
else {
return new Nodes.DoubleConcNode(
return new Nodes.ConcNode.OfDouble(
tree(l.subList(0, l.size() / 2), m),
tree(l.subList(l.size() / 2, l.size()), m));
}

View File

@ -102,7 +102,7 @@ public class IntNodeTest extends OpTestCase {
int i = it.nextInt();
if (it.hasNext()) {
return new Nodes.IntConcNode(Nodes.node(new int[] {i}), degenerateTree(it));
return new Nodes.ConcNode.OfInt(Nodes.node(new int[] {i}), degenerateTree(it));
}
else {
return Nodes.node(new int[] {i});
@ -114,7 +114,7 @@ public class IntNodeTest extends OpTestCase {
return m.apply(l);
}
else {
return new Nodes.IntConcNode(
return new Nodes.ConcNode.OfInt(
tree(l.subList(0, l.size() / 2), m),
tree(l.subList(l.size() / 2, l.size()), m));
}

View File

@ -102,7 +102,7 @@ public class LongNodeTest extends OpTestCase {
long i = it.nextLong();
if (it.hasNext()) {
return new Nodes.LongConcNode(Nodes.node(new long[] {i}), degenerateTree(it));
return new Nodes.ConcNode.OfLong(Nodes.node(new long[] {i}), degenerateTree(it));
}
else {
return Nodes.node(new long[] {i});
@ -114,7 +114,7 @@ public class LongNodeTest extends OpTestCase {
return m.apply(l);
}
else {
return new Nodes.LongConcNode(
return new Nodes.ConcNode.OfLong(
tree(l.subList(0, l.size() / 2), m),
tree(l.subList(l.size() / 2, l.size()), m));
}