From e25108e4dcc5915e96c91ea6c834bc9cf9e1402a Mon Sep 17 00:00:00 2001 From: psandoz Date: Thu, 20 Jun 2013 10:45:46 +0200 Subject: [PATCH] 8016308: Updates to j.u.stream.Node/Nodes Reviewed-by: mduigou Contributed-by: Brian Goetz , Paul Sandoz --- src/share/classes/java/util/stream/Node.java | 343 ++++---- src/share/classes/java/util/stream/Nodes.java | 802 ++++++------------ .../classes/java/util/stream/SliceOps.java | 199 +++-- .../java/util/stream/DoubleNodeTest.java | 4 +- .../java/util/stream/IntNodeTest.java | 4 +- .../java/util/stream/LongNodeTest.java | 4 +- 6 files changed, 529 insertions(+), 827 deletions(-) diff --git a/src/share/classes/java/util/stream/Node.java b/src/share/classes/java/util/stream/Node.java index 3cd765a69..bf4c2c634 100644 --- a/src/share/classes/java/util/stream/Node.java +++ b/src/share/classes/java/util/stream/Node.java @@ -104,6 +104,32 @@ interface Node { 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 truncate(long from, long to, IntFunction generator) { + if (from == 0 && to == count()) + return this; + Spliterator spliterator = spliterator(); + long size = to - from; + Node.Builder 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 { } } - /** - * Specialized {@code Node} for int elements - */ - interface OfInt extends Node { + public interface OfPrimitive, + T_NODE extends OfPrimitive> + extends Node { /** * {@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 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 generator) { + T[] boxed = generator.apply((int) count()); + copyInto(boxed, 0); + return boxed; + } + + /** + * Views this node as a primitive array. + * + *

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} + */ + 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 { /** * {@inheritDoc} @@ -226,38 +323,13 @@ interface Node { } } - /** - * 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 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 { } @Override - default Node.OfInt getChild(int i) { - throw new IndexOutOfBoundsException(); + default Node.OfInt truncate(long from, long to, IntFunction 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. - * - *

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} - */ - 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 { default StreamShape getShape() { return StreamShape.INT_VALUE; } - } /** * Specialized {@code Node} for long elements */ - interface OfLong extends Node { - - /** - * {@inheritDoc} - * - * @return a {@link Spliterator.OfLong} describing the elements of this - * node - */ - @Override - Spliterator.OfLong spliterator(); + interface OfLong extends OfPrimitive { /** * {@inheritDoc} @@ -346,38 +396,13 @@ interface Node { } } - /** - * 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 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 { } @Override - default Node.OfLong getChild(int i) { - throw new IndexOutOfBoundsException(); + default Node.OfLong truncate(long from, long to, IntFunction 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. - * - *

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 { default StreamShape getShape() { return StreamShape.LONG_VALUE; } - - } /** * Specialized {@code Node} for double elements */ - interface OfDouble extends Node { - - /** - * {@inheritDoc} - * - * @return A {@link Spliterator.OfDouble} describing the elements of - * this node - */ - @Override - Spliterator.OfDouble spliterator(); + interface OfDouble extends OfPrimitive { /** * {@inheritDoc} @@ -467,40 +469,15 @@ interface Node { } } - /** - * 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 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 { } @Override - default Node.OfDouble getChild(int i) { - throw new IndexOutOfBoundsException(); + default Node.OfDouble truncate(long from, long to, IntFunction 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. - * - *

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} diff --git a/src/share/classes/java/util/stream/Nodes.java b/src/share/classes/java/util/stream/Nodes.java index e4ccfb96f..f79a88c47 100644 --- a/src/share/classes/java/util/stream/Nodes.java +++ b/src/share/classes/java/util/stream/Nodes.java @@ -33,11 +33,13 @@ import java.util.Objects; import java.util.Spliterator; import java.util.Spliterators; import java.util.concurrent.CountedCompleter; +import java.util.function.BinaryOperator; import java.util.function.Consumer; import java.util.function.DoubleConsumer; import java.util.function.IntConsumer; import java.util.function.IntFunction; import java.util.function.LongConsumer; +import java.util.function.LongFunction; /** * Factory methods for constructing implementations of {@link Node} and @@ -97,131 +99,28 @@ final class Nodes { * * @param the type of elements of the concatenated node * @param shape the shape of the concatenated node to be created - * @param nodes the input nodes + * @param left the left input node + * @param right the right input node * @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") - static Node conc(StreamShape shape, List> nodes) { - int size = nodes.size(); - if (size == 0) - return emptyNode(shape); - else if (size == 1) - return nodes.get(0); - else { - // Create a right-balanced tree when there are more that 2 nodes - switch (shape) { - case REFERENCE: { - List> refNodes = (List>) nodes; - ConcNode c = new ConcNode<>(refNodes.get(size - 2), refNodes.get(size - 1)); - for (int i = size - 3; i >= 0; i--) { - c = new ConcNode<>(refNodes.get(i), c); - } - return c; - } - case INT_VALUE: { - List intNodes = (List) nodes; - IntConcNode c = new IntConcNode(intNodes.get(size - 2), intNodes.get(size - 1)); - for (int i = size - 3; i >= 0; i--) { - c = new IntConcNode(intNodes.get(i), c); - } - return (Node) c; - } - case LONG_VALUE: { - List longNodes = (List) nodes; - LongConcNode c = new LongConcNode(longNodes.get(size - 2), longNodes.get(size - 1)); - for (int i = size - 3; i >= 0; i--) { - c = new LongConcNode(longNodes.get(i), c); - } - return (Node) c; - } - case DOUBLE_VALUE: { - List doubleNodes = (List) nodes; - DoubleConcNode c = new DoubleConcNode(doubleNodes.get(size - 2), doubleNodes.get(size - 1)); - for (int i = size - 3; i >= 0; i--) { - c = new DoubleConcNode(doubleNodes.get(i), c); - } - return (Node) c; - } - default: - throw new IllegalStateException("Unknown shape " + shape); - } - } - - } - - /** - * Truncate a {@link Node}, returning a node describing a subsequence of - * the contents of the input node. - * - * @param 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") - static Node truncateNode(Node input, long from, long to, IntFunction generator) { - StreamShape shape = input.getShape(); - long size = truncatedSize(input.count(), from, to); - if (size == 0) - return emptyNode(shape); - else if (from == 0 && to >= input.count()) - return input; - + static Node conc(StreamShape shape, Node left, Node right) { switch (shape) { - case REFERENCE: { - Spliterator spliterator = input.spliterator(); - Node.Builder 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) 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) 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) nodeBuilder.build(); - } + case REFERENCE: + return new ConcNode<>(left, right); + case INT_VALUE: + return (Node) new ConcNode.OfInt((Node.OfInt) left, (Node.OfInt) right); + case LONG_VALUE: + return (Node) new ConcNode.OfLong((Node.OfLong) left, (Node.OfLong) right); + case DOUBLE_VALUE: + return (Node) new ConcNode.OfDouble((Node.OfDouble) left, (Node.OfDouble) right); 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; - } - // Reference-based node methods /** @@ -422,7 +321,7 @@ final class Nodes { new SizedCollectorTask.OfRef<>(spliterator, helper, array).invoke(); return node(array); } else { - Node node = new CollectorTask<>(helper, generator, spliterator).invoke(); + Node node = new CollectorTask.OfRef<>(helper, generator, spliterator).invoke(); return flattenTree ? flatten(node, generator) : node; } } @@ -460,7 +359,7 @@ final class Nodes { return node(array); } else { - Node.OfInt node = new IntCollectorTask<>(helper, spliterator).invoke(); + Node.OfInt node = new CollectorTask.OfInt<>(helper, spliterator).invoke(); return flattenTree ? flattenInt(node) : node; } } @@ -498,7 +397,7 @@ final class Nodes { return node(array); } else { - Node.OfLong node = new LongCollectorTask<>(helper, spliterator).invoke(); + Node.OfLong node = new CollectorTask.OfLong<>(helper, spliterator).invoke(); return flattenTree ? flattenLong(node) : node; } } @@ -536,7 +435,7 @@ final class Nodes { return node(array); } else { - Node.OfDouble node = new DoubleCollectorTask<>(helper, spliterator).invoke(); + Node.OfDouble node = new CollectorTask.OfDouble<>(helper, spliterator).invoke(); return flattenTree ? flattenDouble(node) : node; } } @@ -763,8 +662,6 @@ final class Nodes { return curSize; } - // Traversable - @Override public void forEach(Consumer consumer) { for (int i = 0; i < curSize; i++) { @@ -829,13 +726,12 @@ final class Nodes { /** * Node class for an internal node with two or more children */ - static final class ConcNode implements Node { - private final Node left; - private final Node right; - + private static abstract class AbstractConcNode> implements Node { + protected final T_NODE left; + protected final T_NODE right; private final long size; - ConcNode(Node left, Node right) { + AbstractConcNode(T_NODE left, T_NODE right) { this.left = left; this.right = right; // The Node count will be required when the Node spliterator is @@ -845,25 +741,37 @@ final class Nodes { this.size = left.count() + right.count(); } - // Node - - @Override - public Spliterator spliterator() { - return new Nodes.InternalNodeSpliterator.OfRef<>(this); - } - @Override public int getChildCount() { return 2; } @Override - public Node getChild(int i) { + public T_NODE getChild(int i) { if (i == 0) return left; if (i == 1) return right; throw new IndexOutOfBoundsException(); } + @Override + public long count() { + return size; + } + } + + static final class ConcNode + extends AbstractConcNode> + implements Node { + + ConcNode(Node left, Node right) { + super(left, right); + } + + @Override + public Spliterator spliterator() { + return new Nodes.InternalNodeSpliterator.OfRef<>(this); + } + @Override public void copyInto(T[] array, int offset) { Objects.requireNonNull(array); @@ -878,17 +786,27 @@ final class Nodes { return array; } - @Override - public long count() { - return size; - } - @Override public void forEach(Consumer consumer) { left.forEach(consumer); right.forEach(consumer); } + @Override + public Node truncate(long from, long to, IntFunction generator) { + if (from == 0 && to == count()) + return this; + long leftCount = left.count(); + if (from >= leftCount) + return right.truncate(from - leftCount, to - leftCount, generator); + else if (to <= leftCount) + return left.truncate(from, to, generator); + else { + return Nodes.conc(getShape(), left.truncate(from, leftCount, generator), + right.truncate(0, to - leftCount, generator)); + } + } + @Override public String toString() { if (count() < 32) { @@ -897,12 +815,92 @@ final class Nodes { return String.format("ConcNode[size=%d]", count()); } } + + private abstract static class OfPrimitive, + T_NODE extends Node.OfPrimitive> + extends AbstractConcNode + implements Node.OfPrimitive { + + OfPrimitive(T_NODE left, T_NODE right) { + super(left, right); + } + + @Override + public void forEach(T_CONS consumer) { + left.forEach(consumer); + right.forEach(consumer); + } + + @Override + public void copyInto(T_ARR array, int offset) { + left.copyInto(array, offset); + right.copyInto(array, offset + (int) left.count()); + } + + @Override + public T_ARR asPrimitiveArray() { + T_ARR array = newArray((int) count()); + copyInto(array, 0); + return array; + } + + @Override + public String toString() { + if (count() < 32) + return String.format("%s[%s.%s]", this.getClass().getName(), left, right); + else + return String.format("%s[size=%d]", this.getClass().getName(), count()); + } + } + + static final class OfInt + extends ConcNode.OfPrimitive + implements Node.OfInt { + + OfInt(Node.OfInt left, Node.OfInt right) { + super(left, right); + } + + @Override + public Spliterator.OfInt spliterator() { + return new InternalNodeSpliterator.OfInt(this); + } + } + + static final class OfLong + extends ConcNode.OfPrimitive + implements Node.OfLong { + + OfLong(Node.OfLong left, Node.OfLong right) { + super(left, right); + } + + @Override + public Spliterator.OfLong spliterator() { + return new InternalNodeSpliterator.OfLong(this); + } + } + + static final class OfDouble + extends ConcNode.OfPrimitive + implements Node.OfDouble { + + OfDouble(Node.OfDouble left, Node.OfDouble right) { + super(left, right); + } + + @Override + public Spliterator.OfDouble spliterator() { + return new InternalNodeSpliterator.OfDouble(this); + } + } } /** Abstract class for spliterator for all internal node classes */ private static abstract class InternalNodeSpliterator, - N extends Node, C> + N extends Node> implements Spliterator { // Node we are pointing to // null if full traversal has occurred @@ -960,7 +958,7 @@ final class Nodes { return null; } - protected final boolean internalTryAdvance(C consumer) { + protected final boolean initTryAdvance() { if (curNode == null) return false; @@ -981,29 +979,12 @@ final class Nodes { else tryAdvanceSpliterator = lastNodeSpliterator; } - - boolean hasNext = tryAdvance(tryAdvanceSpliterator, consumer); - if (!hasNext) { - if (lastNodeSpliterator == null) { - // Advance to the spliterator of the next non-empty leaf node - Node leaf = findNextLeafNode(tryAdvanceStack); - if (leaf != null) { - tryAdvanceSpliterator = (S) leaf.spliterator(); - // Since the node is not-empty the spliterator can be advanced - return tryAdvance(tryAdvanceSpliterator, consumer); - } - } - // No more elements to traverse - curNode = null; - } - return hasNext; + return true; } - protected abstract boolean tryAdvance(S spliterator, C consumer); - @Override @SuppressWarnings("unchecked") - public S trySplit() { + public final S trySplit() { if (curNode == null || tryAdvanceSpliterator != null) return null; // Cannot split if fully or partially traversed else if (lastNodeSpliterator != null) @@ -1024,7 +1005,7 @@ final class Nodes { } @Override - public long estimateSize() { + public final long estimateSize() { if (curNode == null) return 0; @@ -1041,12 +1022,12 @@ final class Nodes { } @Override - public int characteristics() { + public final int characteristics() { return Spliterator.SIZED; } private static final class OfRef - extends InternalNodeSpliterator, Node, Consumer> { + extends InternalNodeSpliterator, Node> { OfRef(Node curNode) { super(curNode); @@ -1054,13 +1035,24 @@ final class Nodes { @Override public boolean tryAdvance(Consumer consumer) { - return internalTryAdvance(consumer); - } + if (!initTryAdvance()) + return false; - @Override - protected boolean tryAdvance(Spliterator spliterator, - Consumer consumer) { - return spliterator.tryAdvance(consumer); + boolean hasNext = tryAdvanceSpliterator.tryAdvance(consumer); + if (!hasNext) { + if (lastNodeSpliterator == null) { + // Advance to the spliterator of the next non-empty leaf node + Node leaf = findNextLeafNode(tryAdvanceStack); + if (leaf != null) { + tryAdvanceSpliterator = leaf.spliterator(); + // Since the node is not-empty the spliterator can be advanced + return tryAdvanceSpliterator.tryAdvance(consumer); + } + } + // No more elements to traverse + curNode = null; + } + return hasNext; } @Override @@ -1085,34 +1077,47 @@ final class Nodes { } } - private static final class OfInt - extends InternalNodeSpliterator - implements Spliterator.OfInt { + private static abstract class OfPrimitive, + N extends Node.OfPrimitive> + extends InternalNodeSpliterator + implements Spliterator.OfPrimitive { - OfInt(Node.OfInt cur) { + OfPrimitive(N cur) { super(cur); } @Override - public boolean tryAdvance(IntConsumer consumer) { - return internalTryAdvance(consumer); - } + public boolean tryAdvance(T_CONS consumer) { + if (!initTryAdvance()) + return false; - @Override - protected boolean tryAdvance(Spliterator.OfInt spliterator, - IntConsumer consumer) { - return spliterator.tryAdvance(consumer); + boolean hasNext = tryAdvanceSpliterator.tryAdvance(consumer); + if (!hasNext) { + if (lastNodeSpliterator == null) { + // Advance to the spliterator of the next non-empty leaf node + N leaf = findNextLeafNode(tryAdvanceStack); + if (leaf != null) { + tryAdvanceSpliterator = leaf.spliterator(); + // Since the node is not-empty the spliterator can be advanced + return tryAdvanceSpliterator.tryAdvance(consumer); + } + } + // No more elements to traverse + curNode = null; + } + return hasNext; } @Override - public void forEachRemaining(IntConsumer consumer) { + public void forEachRemaining(T_CONS consumer) { if (curNode == null) return; if (tryAdvanceSpliterator == null) { if (lastNodeSpliterator == null) { - Deque stack = initStack(); - Node.OfInt leaf; + Deque stack = initStack(); + N leaf; while ((leaf = findNextLeafNode(stack)) != null) { leaf.forEach(consumer); } @@ -1126,86 +1131,31 @@ final class Nodes { } } - private static final class OfLong - extends InternalNodeSpliterator - implements Spliterator.OfLong { + private static final class OfInt + extends OfPrimitive + implements Spliterator.OfInt { - OfLong(Node.OfLong cur) { + OfInt(Node.OfInt cur) { super(cur); } + } - @Override - public boolean tryAdvance(LongConsumer consumer) { - return internalTryAdvance(consumer); - } - - @Override - protected boolean tryAdvance(Spliterator.OfLong spliterator, - LongConsumer consumer) { - return spliterator.tryAdvance(consumer); - } - - @Override - public void forEachRemaining(LongConsumer consumer) { - if (curNode == null) - return; + private static final class OfLong + extends OfPrimitive + implements Spliterator.OfLong { - if (tryAdvanceSpliterator == null) { - if (lastNodeSpliterator == null) { - Deque stack = initStack(); - Node.OfLong leaf; - while ((leaf = findNextLeafNode(stack)) != null) { - leaf.forEach(consumer); - } - curNode = null; - } - else - lastNodeSpliterator.forEachRemaining(consumer); - } - else - while(tryAdvance(consumer)) { } + OfLong(Node.OfLong cur) { + super(cur); } } private static final class OfDouble - extends InternalNodeSpliterator + extends OfPrimitive implements Spliterator.OfDouble { OfDouble(Node.OfDouble cur) { super(cur); } - - @Override - public boolean tryAdvance(DoubleConsumer consumer) { - return internalTryAdvance(consumer); - } - - @Override - protected boolean tryAdvance(Spliterator.OfDouble spliterator, - DoubleConsumer consumer) { - return spliterator.tryAdvance(consumer); - } - - @Override - public void forEachRemaining(DoubleConsumer consumer) { - if (curNode == null) - return; - - if (tryAdvanceSpliterator == null) { - if (lastNodeSpliterator == null) { - Deque stack = initStack(); - Node.OfDouble leaf; - while ((leaf = findNextLeafNode(stack)) != null) { - leaf.forEach(consumer); - } - curNode = null; - } - else - lastNodeSpliterator.forEachRemaining(consumer); - } - else - while(tryAdvance(consumer)) { } - } } } @@ -1330,47 +1280,6 @@ final class Nodes { private static final long[] EMPTY_LONG_ARRAY = new long[0]; private static final double[] EMPTY_DOUBLE_ARRAY = new double[0]; - private abstract static class AbstractPrimitiveConcNode> - implements Node { - final N left; - final N right; - final long size; - - AbstractPrimitiveConcNode(N left, N right) { - this.left = left; - this.right = right; - // The Node count will be required when the Node spliterator is - // obtained and it is cheaper to aggressively calculate bottom up as - // the tree is built rather than later on by traversing the tree - this.size = left.count() + right.count(); - } - - @Override - public int getChildCount() { - return 2; - } - - @Override - public N getChild(int i) { - if (i == 0) return left; - if (i == 1) return right; - throw new IndexOutOfBoundsException(); - } - - @Override - public long count() { - return size; - } - - @Override - public String toString() { - if (count() < 32) - return String.format("%s[%s.%s]", this.getClass().getName(), left, right); - else - return String.format("%s[size=%d]", this.getClass().getName(), count()); - } - } - private static class IntArrayNode implements Node.OfInt { final int[] array; int curSize; @@ -1535,105 +1444,6 @@ final class Nodes { } } - static final class IntConcNode - extends AbstractPrimitiveConcNode - implements Node.OfInt { - - IntConcNode(Node.OfInt left, Node.OfInt right) { - super(left, right); - } - - @Override - public void forEach(IntConsumer consumer) { - left.forEach(consumer); - right.forEach(consumer); - } - - @Override - public Spliterator.OfInt spliterator() { - return new InternalNodeSpliterator.OfInt(this); - } - - @Override - public void copyInto(int[] array, int offset) { - left.copyInto(array, offset); - right.copyInto(array, offset + (int) left.count()); - } - - @Override - public int[] asPrimitiveArray() { - int[] array = new int[(int) count()]; - copyInto(array, 0); - return array; - } - } - - static final class LongConcNode - extends AbstractPrimitiveConcNode - implements Node.OfLong { - - LongConcNode(Node.OfLong left, Node.OfLong right) { - super(left, right); - } - - @Override - public void forEach(LongConsumer consumer) { - left.forEach(consumer); - right.forEach(consumer); - } - - @Override - public Spliterator.OfLong spliterator() { - return new InternalNodeSpliterator.OfLong(this); - } - - @Override - public void copyInto(long[] array, int offset) { - left.copyInto(array, offset); - right.copyInto(array, offset + (int) left.count()); - } - - @Override - public long[] asPrimitiveArray() { - long[] array = new long[(int) count()]; - copyInto(array, 0); - return array; - } - } - - static final class DoubleConcNode - extends AbstractPrimitiveConcNode - implements Node.OfDouble { - - DoubleConcNode(Node.OfDouble left, Node.OfDouble right) { - super(left, right); - } - - @Override - public void forEach(DoubleConsumer consumer) { - left.forEach(consumer); - right.forEach(consumer); - } - - @Override - public Spliterator.OfDouble spliterator() { - return new InternalNodeSpliterator.OfDouble(this); - } - - @Override - public void copyInto(double[] array, int offset) { - left.copyInto(array, offset); - right.copyInto(array, offset + (int) left.count()); - } - - @Override - public double[] asPrimitiveArray() { - double[] array = new double[(int) count()]; - copyInto(array, 0); - return array; - } - } - private static final class IntFixedNodeBuilder extends IntArrayNode implements Node.Builder.OfInt { @@ -2245,23 +2055,25 @@ final class Nodes { } } - private static final class OfInt - extends ToArrayTask { - private final int[] array; + private static class OfPrimitive, + T_NODE extends Node.OfPrimitive> + extends ToArrayTask> { + private final T_ARR array; - private OfInt(Node.OfInt node, int[] array, int offset) { + private OfPrimitive(T_NODE node, T_ARR array, int offset) { super(node, offset); this.array = array; } - private OfInt(OfInt parent, Node.OfInt node, int offset) { + private OfPrimitive(OfPrimitive parent, T_NODE node, int offset) { super(parent, node, offset); this.array = parent.array; } @Override - OfInt makeChild(int childIndex, int offset) { - return new OfInt(this, node.getChild(childIndex), offset); + OfPrimitive makeChild(int childIndex, int offset) { + return new OfPrimitive<>(this, node.getChild(childIndex), offset); } @Override @@ -2270,198 +2082,98 @@ final class Nodes { } } - private static final class OfLong - extends ToArrayTask { - private final long[] array; - - private OfLong(Node.OfLong node, long[] array, int offset) { - super(node, offset); - this.array = array; - } - - private OfLong(OfLong parent, Node.OfLong node, int offset) { - super(parent, node, offset); - this.array = parent.array; - } - - @Override - OfLong makeChild(int childIndex, int offset) { - return new OfLong(this, node.getChild(childIndex), offset); + private static final class OfInt + extends OfPrimitive { + private OfInt(Node.OfInt node, int[] array, int offset) { + super(node, array, offset); } + } - @Override - void copyNodeToArray() { - node.copyInto(array, offset); + private static final class OfLong + extends OfPrimitive { + private OfLong(Node.OfLong node, long[] array, int offset) { + super(node, array, offset); } } private static final class OfDouble - extends ToArrayTask { - private final double[] array; - + extends OfPrimitive { private OfDouble(Node.OfDouble node, double[] array, int offset) { - super(node, offset); - this.array = array; - } - - private OfDouble(OfDouble parent, Node.OfDouble node, int offset) { - super(parent, node, offset); - this.array = parent.array; - } - - @Override - OfDouble makeChild(int childIndex, int offset) { - return new OfDouble(this, node.getChild(childIndex), offset); - } - - @Override - void copyNodeToArray() { - node.copyInto(array, offset); + super(node, array, offset); } } } - private static final class CollectorTask - extends AbstractTask, CollectorTask> { - private final PipelineHelper helper; - private final IntFunction generator; + private static class CollectorTask, T_BUILDER extends Node.Builder> + extends AbstractTask> { + protected final PipelineHelper helper; + protected final LongFunction builderFactory; + protected final BinaryOperator concFactory; CollectorTask(PipelineHelper helper, - IntFunction generator, - Spliterator spliterator) { + Spliterator spliterator, + LongFunction builderFactory, + BinaryOperator concFactory) { super(helper, spliterator); this.helper = helper; - this.generator = generator; + this.builderFactory = builderFactory; + this.concFactory = concFactory; } - CollectorTask(CollectorTask parent, Spliterator spliterator) { + CollectorTask(CollectorTask parent, + Spliterator spliterator) { super(parent, spliterator); helper = parent.helper; - generator = parent.generator; + builderFactory = parent.builderFactory; + concFactory = parent.concFactory; } @Override - protected CollectorTask makeChild(Spliterator spliterator) { + protected CollectorTask makeChild(Spliterator spliterator) { return new CollectorTask<>(this, spliterator); } @Override - protected Node doLeaf() { - Node.Builder builder - = builder(helper.exactOutputSizeIfKnown(spliterator), - generator); - return helper.wrapAndCopyInto(builder, spliterator).build(); + protected T_NODE doLeaf() { + T_BUILDER builder = builderFactory.apply(helper.exactOutputSizeIfKnown(spliterator)); + return (T_NODE) helper.wrapAndCopyInto(builder, spliterator).build(); } @Override public void onCompletion(CountedCompleter caller) { - if (!isLeaf()) { - setLocalResult(new ConcNode<>(leftChild.getLocalResult(), rightChild.getLocalResult())); - } + if (!isLeaf()) + setLocalResult(concFactory.apply(leftChild.getLocalResult(), rightChild.getLocalResult())); super.onCompletion(caller); } - } - - private static final class IntCollectorTask - extends AbstractTask> { - private final PipelineHelper helper; - - IntCollectorTask(PipelineHelper helper, Spliterator spliterator) { - super(helper, spliterator); - this.helper = helper; - } - IntCollectorTask(IntCollectorTask parent, Spliterator spliterator) { - super(parent, spliterator); - helper = parent.helper; - } - - @Override - protected IntCollectorTask makeChild(Spliterator spliterator) { - return new IntCollectorTask<>(this, spliterator); - } - - @Override - protected Node.OfInt doLeaf() { - Node.Builder.OfInt builder = intBuilder(helper.exactOutputSizeIfKnown(spliterator)); - return helper.wrapAndCopyInto(builder, spliterator).build(); - } - - @Override - public void onCompletion(CountedCompleter caller) { - if (!isLeaf()) { - setLocalResult(new IntConcNode(leftChild.getLocalResult(), rightChild.getLocalResult())); + private static final class OfRef + extends CollectorTask, Node.Builder> { + OfRef(PipelineHelper helper, + IntFunction generator, + Spliterator spliterator) { + super(helper, spliterator, s -> builder(s, generator), ConcNode::new); } - super.onCompletion(caller); - } - } - - private static final class LongCollectorTask - extends AbstractTask> { - private final PipelineHelper helper; - - LongCollectorTask(PipelineHelper helper, Spliterator spliterator) { - super(helper, spliterator); - this.helper = helper; - } - - LongCollectorTask(LongCollectorTask parent, Spliterator spliterator) { - super(parent, spliterator); - helper = parent.helper; - } - - @Override - protected LongCollectorTask makeChild(Spliterator spliterator) { - return new LongCollectorTask<>(this, spliterator); } - @Override - protected Node.OfLong doLeaf() { - Node.Builder.OfLong builder = longBuilder(helper.exactOutputSizeIfKnown(spliterator)); - return helper.wrapAndCopyInto(builder, spliterator).build(); - } - - @Override - public void onCompletion(CountedCompleter caller) { - if (!isLeaf()) { - setLocalResult(new LongConcNode(leftChild.getLocalResult(), rightChild.getLocalResult())); + private static final class OfInt + extends CollectorTask { + OfInt(PipelineHelper helper, Spliterator spliterator) { + super(helper, spliterator, Nodes::intBuilder, ConcNode.OfInt::new); } - super.onCompletion(caller); - } - } - - private static final class DoubleCollectorTask - extends AbstractTask> { - private final PipelineHelper helper; - - DoubleCollectorTask(PipelineHelper helper, Spliterator spliterator) { - super(helper, spliterator); - this.helper = helper; } - DoubleCollectorTask(DoubleCollectorTask parent, Spliterator spliterator) { - super(parent, spliterator); - helper = parent.helper; - } - - @Override - protected DoubleCollectorTask makeChild(Spliterator spliterator) { - return new DoubleCollectorTask<>(this, spliterator); - } - - @Override - protected Node.OfDouble doLeaf() { - Node.Builder.OfDouble builder - = doubleBuilder(helper.exactOutputSizeIfKnown(spliterator)); - return helper.wrapAndCopyInto(builder, spliterator).build(); + private static final class OfLong + extends CollectorTask { + OfLong(PipelineHelper helper, Spliterator spliterator) { + super(helper, spliterator, Nodes::longBuilder, ConcNode.OfLong::new); + } } - @Override - public void onCompletion(CountedCompleter caller) { - if (!isLeaf()) { - setLocalResult(new DoubleConcNode(leftChild.getLocalResult(), rightChild.getLocalResult())); + private static final class OfDouble + extends CollectorTask { + OfDouble(PipelineHelper helper, Spliterator spliterator) { + super(helper, spliterator, Nodes::doubleBuilder, ConcNode.OfDouble::new); } - super.onCompletion(caller); } } } diff --git a/src/share/classes/java/util/stream/SliceOps.java b/src/share/classes/java/util/stream/SliceOps.java index 4dc26ce04..31c6bd824 100644 --- a/src/share/classes/java/util/stream/SliceOps.java +++ b/src/share/classes/java/util/stream/SliceOps.java @@ -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 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 Node truncateNode(Node input, long from, long to, IntFunction 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 spliterator = input.spliterator(); + Node.Builder 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) 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) 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) nodeBuilder.build(); + } + default: + throw new IllegalStateException("Unknown shape " + shape); } } - } - // @@@ Currently unused -- optimization for when all sizes are known -// private static class SizedSliceTask extends AbstractShortCircuitTask, SizedSliceTask> { -// private final int targetOffset, targetSize; -// private final int offset, size; -// -// private SizedSliceTask(ParallelPipelineHelper helper, int offset, int size) { -// super(helper); -// targetOffset = offset; -// targetSize = size; -// this.offset = 0; -// this.size = spliterator.getSizeIfKnown(); -// } -// -// private SizedSliceTask(SizedSliceTask parent, Spliterator 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 sibling = parent.children; sibling != null; sibling = sibling.nextSibling) -// siblingSizes += sibling.size; -// size = spliterator.getSizeIfKnown(); -// offset = parent.offset + siblingSizes; -// } -// -// @Override -// protected SizedSliceTask makeChild(Spliterator spliterator) { -// return new SizedSliceTask<>(this, spliterator); -// } -// -// @Override -// protected Node getEmptyResult() { -// return Nodes.emptyNode(); -// } -// -// @Override -// public boolean taskCanceled() { -// if (offset > targetOffset+targetSize || offset+size < targetOffset) -// return true; -// else -// return super.taskCanceled(); -// } -// -// @Override -// protected Node 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.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 builder = Nodes.makeBuilder(truncatedSize); -// Sink wrappedSink = helper.wrapSink(builder); -// wrappedSink.begin(truncatedSize); -// Iterator iterator = spliterator.iterator(); -// for (int i=0; i caller) { -// if (!isLeaf()) { -// Node result = null; -// for (SizedSliceTask child = children.nextSibling; child != null; child = child.nextSibling) { -// Node 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); -// } -// } -// } + 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. + *

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. + * + *

The result may be a concatenated node, the input sole node if the size + * of the list is 1, or an empty node. + * + * @param 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 Node conc(StreamShape shape, List> 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> refNodes = (List>) nodes; + Node 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; + } + } + + } } diff --git a/test/java/util/stream/boottest/java/util/stream/DoubleNodeTest.java b/test/java/util/stream/boottest/java/util/stream/DoubleNodeTest.java index f0d538098..23eee7d74 100644 --- a/test/java/util/stream/boottest/java/util/stream/DoubleNodeTest.java +++ b/test/java/util/stream/boottest/java/util/stream/DoubleNodeTest.java @@ -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)); } diff --git a/test/java/util/stream/boottest/java/util/stream/IntNodeTest.java b/test/java/util/stream/boottest/java/util/stream/IntNodeTest.java index 7b91f8ea8..17e50bb1e 100644 --- a/test/java/util/stream/boottest/java/util/stream/IntNodeTest.java +++ b/test/java/util/stream/boottest/java/util/stream/IntNodeTest.java @@ -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)); } diff --git a/test/java/util/stream/boottest/java/util/stream/LongNodeTest.java b/test/java/util/stream/boottest/java/util/stream/LongNodeTest.java index 877517ebd..6d1392316 100644 --- a/test/java/util/stream/boottest/java/util/stream/LongNodeTest.java +++ b/test/java/util/stream/boottest/java/util/stream/LongNodeTest.java @@ -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)); } -- GitLab