8016308: Updates to j.u.stream.Node/Nodes
authorpsandoz
Thu, 20 Jun 2013 10:45:46 +0200
changeset 18527 882b39a21471
parent 18294 364814dc7391
child 18528 9b02581cf079
8016308: Updates to j.u.stream.Node/Nodes Reviewed-by: mduigou Contributed-by: Brian Goetz <brian.goetz@oracle.com>, Paul Sandoz <paul.sandoz@oracle.com>
jdk/src/share/classes/java/util/stream/Node.java
jdk/src/share/classes/java/util/stream/Nodes.java
jdk/src/share/classes/java/util/stream/SliceOps.java
jdk/test/java/util/stream/boottest/java/util/stream/DoubleNodeTest.java
jdk/test/java/util/stream/boottest/java/util/stream/IntNodeTest.java
jdk/test/java/util/stream/boottest/java/util/stream/LongNodeTest.java
--- a/jdk/src/share/classes/java/util/stream/Node.java	Wed Jun 19 17:41:12 2013 -0700
+++ b/jdk/src/share/classes/java/util/stream/Node.java	Thu Jun 20 10:45:46 2013 +0200
@@ -105,6 +105,32 @@
     }
 
     /**
+     * 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.
      *
      * <p>Depending on the underlying implementation, this may return a
@@ -192,19 +218,90 @@
         }
     }
 
-    /**
-     * 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.OfPrimitive} describing the elements of
+         *         this node
+         */
+        @Override
+        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}
          *
-         * @return a {@link Spliterator.OfInt} describing the elements of this
-         *         node
+         * @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
-        Spliterator.OfInt spliterator();
+        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}
@@ -227,37 +324,12 @@
         }
 
         /**
-         * 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 @@
         }
 
         @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 @@
         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}
@@ -347,37 +397,12 @@
         }
 
         /**
-         * 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 @@
         }
 
         @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 @@
         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 @@
             }
         }
 
-        /**
-         * 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 @@
         }
 
         @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}
--- a/jdk/src/share/classes/java/util/stream/Nodes.java	Wed Jun 19 17:41:12 2013 -0700
+++ b/jdk/src/share/classes/java/util/stream/Nodes.java	Thu Jun 20 10:45:46 2013 +0200
@@ -33,11 +33,13 @@
 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 @@
      *
      * @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
+     * @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 <T> Node<T> conc(StreamShape shape, List<? extends Node<T>> 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<Node<T>> refNodes = (List<Node<T>>) nodes;
-                    ConcNode<T> 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<? extends Node.OfInt> intNodes = (List<? extends Node.OfInt>) 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<T>) c;
-                }
-                case LONG_VALUE: {
-                    List<? extends Node.OfLong> longNodes = (List<? extends Node.OfLong>) 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<T>) c;
-                }
-                case DOUBLE_VALUE: {
-                    List<? extends Node.OfDouble> doubleNodes = (List<? extends Node.OfDouble>) 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<T>) 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 <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")
-    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 emptyNode(shape);
-        else if (from == 0 && to >= input.count())
-            return input;
-
+    static <T> Node<T> conc(StreamShape shape, Node<T> left, Node<T> right) {
         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();
-            }
+            case REFERENCE:
+                return new ConcNode<>(left, right);
+            case INT_VALUE:
+                return (Node<T>) new ConcNode.OfInt((Node.OfInt) left, (Node.OfInt) right);
+            case LONG_VALUE:
+                return (Node<T>) new ConcNode.OfLong((Node.OfLong) left, (Node.OfLong) right);
+            case DOUBLE_VALUE:
+                return (Node<T>) 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 @@
             new SizedCollectorTask.OfRef<>(spliterator, helper, array).invoke();
             return node(array);
         } else {
-            Node<P_OUT> node = new CollectorTask<>(helper, generator, spliterator).invoke();
+            Node<P_OUT> node = new CollectorTask.OfRef<>(helper, generator, spliterator).invoke();
             return flattenTree ? flatten(node, generator) : node;
         }
     }
@@ -460,7 +359,7 @@
             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 @@
             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 @@
             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 @@
             return curSize;
         }
 
-        // Traversable
-
         @Override
         public void forEach(Consumer<? super T> consumer) {
             for (int i = 0; i < curSize; i++) {
@@ -829,13 +726,12 @@
     /**
      * Node class for an internal node with two or more children
      */
-    static final class ConcNode<T> implements Node<T> {
-        private final Node<T> left;
-        private final Node<T> right;
-
+    private static abstract class AbstractConcNode<T, T_NODE extends Node<T>> implements Node<T> {
+        protected final T_NODE left;
+        protected final T_NODE right;
         private final long size;
 
-        ConcNode(Node<T> left, Node<T> 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,26 +741,38 @@
             this.size = left.count() + right.count();
         }
 
-        // Node
-
-        @Override
-        public Spliterator<T> spliterator() {
-            return new Nodes.InternalNodeSpliterator.OfRef<>(this);
-        }
-
         @Override
         public int getChildCount() {
             return 2;
         }
 
         @Override
-        public Node<T> 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<T>
+            extends AbstractConcNode<T, Node<T>>
+            implements Node<T> {
+
+        ConcNode(Node<T> left, Node<T> right) {
+            super(left, right);
+        }
+
+        @Override
+        public Spliterator<T> spliterator() {
+            return new Nodes.InternalNodeSpliterator.OfRef<>(this);
+        }
+
+        @Override
         public void copyInto(T[] array, int offset) {
             Objects.requireNonNull(array);
             left.copyInto(array, offset);
@@ -879,14 +787,24 @@
         }
 
         @Override
-        public long count() {
-            return size;
+        public void forEach(Consumer<? super T> consumer) {
+            left.forEach(consumer);
+            right.forEach(consumer);
         }
 
         @Override
-        public void forEach(Consumer<? super T> consumer) {
-            left.forEach(consumer);
-            right.forEach(consumer);
+        public Node<T> truncate(long from, long to, IntFunction<T[]> 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
@@ -897,12 +815,92 @@
                 return String.format("ConcNode[size=%d]", count());
             }
         }
+
+        private abstract static class OfPrimitive<E, T_CONS, T_ARR,
+                                                  T_SPLITR extends Spliterator.OfPrimitive<E, T_CONS, T_SPLITR>,
+                                                  T_NODE extends Node.OfPrimitive<E, T_CONS, T_ARR, T_SPLITR, T_NODE>>
+                extends AbstractConcNode<E, T_NODE>
+                implements Node.OfPrimitive<E, T_CONS, T_ARR, T_SPLITR, T_NODE> {
+
+            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<Integer, IntConsumer, int[], Spliterator.OfInt, Node.OfInt>
+                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<Long, LongConsumer, long[], Spliterator.OfLong, Node.OfLong>
+                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<Double, DoubleConsumer, double[], Spliterator.OfDouble, Node.OfDouble>
+                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<T,
                                                           S extends Spliterator<T>,
-                                                          N extends Node<T>, C>
+                                                          N extends Node<T>>
             implements Spliterator<T> {
         // Node we are pointing to
         // null if full traversal has occurred
@@ -960,7 +958,7 @@
             return null;
         }
 
-        protected final boolean internalTryAdvance(C consumer) {
+        protected final boolean initTryAdvance() {
             if (curNode == null)
                 return false;
 
@@ -981,29 +979,12 @@
                 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<T> 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 @@
         }
 
         @Override
-        public long estimateSize() {
+        public final long estimateSize() {
             if (curNode == null)
                 return 0;
 
@@ -1041,12 +1022,12 @@
         }
 
         @Override
-        public int characteristics() {
+        public final int characteristics() {
             return Spliterator.SIZED;
         }
 
         private static final class OfRef<T>
-                extends InternalNodeSpliterator<T, Spliterator<T>, Node<T>, Consumer<? super T>> {
+                extends InternalNodeSpliterator<T, Spliterator<T>, Node<T>> {
 
             OfRef(Node<T> curNode) {
                 super(curNode);
@@ -1054,13 +1035,24 @@
 
             @Override
             public boolean tryAdvance(Consumer<? super T> consumer) {
-                return internalTryAdvance(consumer);
-            }
+                if (!initTryAdvance())
+                    return false;
 
-            @Override
-            protected boolean tryAdvance(Spliterator<T> spliterator,
-                                         Consumer<? super T> 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<T> 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 @@
             }
         }
 
-        private static final class OfInt
-                extends InternalNodeSpliterator<Integer, Spliterator.OfInt, Node.OfInt, IntConsumer>
-                implements Spliterator.OfInt {
+        private static abstract class OfPrimitive<T, T_CONS, T_ARR,
+                                                  T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>,
+                                                  N extends Node.OfPrimitive<T, T_CONS, T_ARR, T_SPLITR, N>>
+                extends InternalNodeSpliterator<T, T_SPLITR, N>
+                implements Spliterator.OfPrimitive<T, T_CONS, T_SPLITR> {
 
-            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;
+
+                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
-            protected boolean tryAdvance(Spliterator.OfInt spliterator,
-                                         IntConsumer consumer) {
-                return spliterator.tryAdvance(consumer);
-            }
-
-            @Override
-            public void forEachRemaining(IntConsumer consumer) {
+            public void forEachRemaining(T_CONS consumer) {
                 if (curNode == null)
                     return;
 
                 if (tryAdvanceSpliterator == null) {
                     if (lastNodeSpliterator == null) {
-                        Deque<Node.OfInt> stack = initStack();
-                        Node.OfInt leaf;
+                        Deque<N> stack = initStack();
+                        N leaf;
                         while ((leaf = findNextLeafNode(stack)) != null) {
                             leaf.forEach(consumer);
                         }
@@ -1126,86 +1131,31 @@
             }
         }
 
+        private static final class OfInt
+                extends OfPrimitive<Integer, IntConsumer, int[], Spliterator.OfInt, Node.OfInt>
+                implements Spliterator.OfInt {
+
+            OfInt(Node.OfInt cur) {
+                super(cur);
+            }
+        }
+
         private static final class OfLong
-                extends InternalNodeSpliterator<Long, Spliterator.OfLong, Node.OfLong, LongConsumer>
+                extends OfPrimitive<Long, LongConsumer, long[], Spliterator.OfLong, Node.OfLong>
                 implements Spliterator.OfLong {
 
             OfLong(Node.OfLong 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;
-
-                if (tryAdvanceSpliterator == null) {
-                    if (lastNodeSpliterator == null) {
-                        Deque<Node.OfLong> stack = initStack();
-                        Node.OfLong leaf;
-                        while ((leaf = findNextLeafNode(stack)) != null) {
-                            leaf.forEach(consumer);
-                        }
-                        curNode = null;
-                    }
-                    else
-                        lastNodeSpliterator.forEachRemaining(consumer);
-                }
-                else
-                    while(tryAdvance(consumer)) { }
-            }
         }
 
         private static final class OfDouble
-                extends InternalNodeSpliterator<Double, Spliterator.OfDouble, Node.OfDouble, DoubleConsumer>
+                extends OfPrimitive<Double, DoubleConsumer, double[], Spliterator.OfDouble, Node.OfDouble>
                 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<Node.OfDouble> 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 @@
     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<E, N extends Node<E>>
-            implements Node<E> {
-        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 @@
         }
     }
 
-    static final class IntConcNode
-            extends AbstractPrimitiveConcNode<Integer, Node.OfInt>
-            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<Long, Node.OfLong>
-            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<Double, Node.OfDouble>
-            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,48 +2055,25 @@
             }
         }
 
-        private static final class OfInt
-                extends ToArrayTask<Integer, Node.OfInt, OfInt> {
-            private final int[] array;
+        private static class OfPrimitive<T, T_CONS, T_ARR,
+                                         T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>,
+                                         T_NODE extends Node.OfPrimitive<T, T_CONS, T_ARR, T_SPLITR, T_NODE>>
+                extends ToArrayTask<T, T_NODE, OfPrimitive<T, T_CONS, T_ARR, T_SPLITR, T_NODE>> {
+            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<T, T_CONS, T_ARR, T_SPLITR, T_NODE> 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);
-            }
-
-            @Override
-            void copyNodeToArray() {
-                node.copyInto(array, offset);
-            }
-        }
-
-        private static final class OfLong
-                extends ToArrayTask<Long, Node.OfLong, OfLong> {
-            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);
+            OfPrimitive<T, T_CONS, T_ARR, T_SPLITR, T_NODE> makeChild(int childIndex, int offset) {
+                return new OfPrimitive<>(this, node.getChild(childIndex), offset);
             }
 
             @Override
@@ -2295,173 +2082,98 @@
             }
         }
 
-        private static final class OfDouble
-                extends ToArrayTask<Double, Node.OfDouble, OfDouble> {
-            private final double[] array;
-
-            private OfDouble(Node.OfDouble node, double[] array, int offset) {
-                super(node, offset);
-                this.array = array;
+        private static final class OfInt
+                extends OfPrimitive<Integer, IntConsumer, int[], Spliterator.OfInt, Node.OfInt> {
+            private OfInt(Node.OfInt node, int[] array, int offset) {
+                super(node, array, offset);
             }
+        }
 
-            private OfDouble(OfDouble parent, Node.OfDouble node, int offset) {
-                super(parent, node, offset);
-                this.array = parent.array;
+        private static final class OfLong
+                extends OfPrimitive<Long, LongConsumer, long[], Spliterator.OfLong, Node.OfLong> {
+            private OfLong(Node.OfLong node, long[] array, int offset) {
+                super(node, array, offset);
             }
+        }
 
-            @Override
-            OfDouble makeChild(int childIndex, int offset) {
-                return new OfDouble(this, node.getChild(childIndex), offset);
-            }
-
-            @Override
-            void copyNodeToArray() {
-                node.copyInto(array, offset);
+        private static final class OfDouble
+                extends OfPrimitive<Double, DoubleConsumer, double[], Spliterator.OfDouble, Node.OfDouble> {
+            private OfDouble(Node.OfDouble node, double[] array, int offset) {
+                super(node, array, offset);
             }
         }
     }
 
-    private static final class CollectorTask<P_IN, P_OUT>
-            extends AbstractTask<P_IN, P_OUT, Node<P_OUT>, CollectorTask<P_IN, P_OUT>> {
-        private final PipelineHelper<P_OUT> helper;
-        private final IntFunction<P_OUT[]> generator;
+    private static class CollectorTask<P_IN, P_OUT, T_NODE extends Node<P_OUT>, T_BUILDER extends Node.Builder<P_OUT>>
+            extends AbstractTask<P_IN, P_OUT, T_NODE, CollectorTask<P_IN, P_OUT, T_NODE, T_BUILDER>> {
+        protected final PipelineHelper<P_OUT> helper;
+        protected final LongFunction<T_BUILDER> builderFactory;
+        protected final BinaryOperator<T_NODE> concFactory;
 
         CollectorTask(PipelineHelper<P_OUT> helper,
-                      IntFunction<P_OUT[]> generator,
-                      Spliterator<P_IN> spliterator) {
+                      Spliterator<P_IN> spliterator,
+                      LongFunction<T_BUILDER> builderFactory,
+                      BinaryOperator<T_NODE> concFactory) {
             super(helper, spliterator);
             this.helper = helper;
-            this.generator = generator;
+            this.builderFactory = builderFactory;
+            this.concFactory = concFactory;
         }
 
-        CollectorTask(CollectorTask<P_IN, P_OUT> parent, Spliterator<P_IN> spliterator) {
+        CollectorTask(CollectorTask<P_IN, P_OUT, T_NODE, T_BUILDER> parent,
+                      Spliterator<P_IN> spliterator) {
             super(parent, spliterator);
             helper = parent.helper;
-            generator = parent.generator;
+            builderFactory = parent.builderFactory;
+            concFactory = parent.concFactory;
         }
 
         @Override
-        protected CollectorTask<P_IN, P_OUT> makeChild(Spliterator<P_IN> spliterator) {
+        protected CollectorTask<P_IN, P_OUT, T_NODE, T_BUILDER> makeChild(Spliterator<P_IN> spliterator) {
             return new CollectorTask<>(this, spliterator);
         }
 
         @Override
-        protected Node<P_OUT> doLeaf() {
-            Node.Builder<P_OUT> 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<P_IN>
-            extends AbstractTask<P_IN, Integer, Node.OfInt, IntCollectorTask<P_IN>> {
-        private final PipelineHelper<Integer> helper;
-
-        IntCollectorTask(PipelineHelper<Integer> helper, Spliterator<P_IN> spliterator) {
-            super(helper, spliterator);
-            this.helper = helper;
-        }
-
-        IntCollectorTask(IntCollectorTask<P_IN> parent, Spliterator<P_IN> spliterator) {
-            super(parent, spliterator);
-            helper = parent.helper;
-        }
 
-        @Override
-        protected IntCollectorTask<P_IN> makeChild(Spliterator<P_IN> 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<P_IN, P_OUT>
+                extends CollectorTask<P_IN, P_OUT, Node<P_OUT>, Node.Builder<P_OUT>> {
+            OfRef(PipelineHelper<P_OUT> helper,
+                  IntFunction<P_OUT[]> generator,
+                  Spliterator<P_IN> spliterator) {
+                super(helper, spliterator, s -> builder(s, generator), ConcNode::new);
             }
-            super.onCompletion(caller);
-        }
-    }
-
-    private static final class LongCollectorTask<P_IN>
-            extends AbstractTask<P_IN, Long, Node.OfLong, LongCollectorTask<P_IN>> {
-        private final PipelineHelper<Long> helper;
-
-        LongCollectorTask(PipelineHelper<Long> helper, Spliterator<P_IN> spliterator) {
-            super(helper, spliterator);
-            this.helper = helper;
         }
 
-        LongCollectorTask(LongCollectorTask<P_IN> parent, Spliterator<P_IN> spliterator) {
-            super(parent, spliterator);
-            helper = parent.helper;
-        }
-
-        @Override
-        protected LongCollectorTask<P_IN> makeChild(Spliterator<P_IN> 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();
+        private static final class OfInt<P_IN>
+                extends CollectorTask<P_IN, Integer, Node.OfInt, Node.Builder.OfInt> {
+            OfInt(PipelineHelper<Integer> helper, Spliterator<P_IN> spliterator) {
+                super(helper, spliterator, Nodes::intBuilder, ConcNode.OfInt::new);
+            }
         }
 
-        @Override
-        public void onCompletion(CountedCompleter caller) {
-            if (!isLeaf()) {
-                setLocalResult(new LongConcNode(leftChild.getLocalResult(), rightChild.getLocalResult()));
+        private static final class OfLong<P_IN>
+                extends CollectorTask<P_IN, Long, Node.OfLong, Node.Builder.OfLong> {
+            OfLong(PipelineHelper<Long> helper, Spliterator<P_IN> spliterator) {
+                super(helper, spliterator, Nodes::longBuilder, ConcNode.OfLong::new);
             }
-            super.onCompletion(caller);
-        }
-    }
-
-    private static final class DoubleCollectorTask<P_IN>
-            extends AbstractTask<P_IN, Double, Node.OfDouble, DoubleCollectorTask<P_IN>> {
-        private final PipelineHelper<Double> helper;
-
-        DoubleCollectorTask(PipelineHelper<Double> helper, Spliterator<P_IN> spliterator) {
-            super(helper, spliterator);
-            this.helper = helper;
         }
 
-        DoubleCollectorTask(DoubleCollectorTask<P_IN> parent, Spliterator<P_IN> spliterator) {
-            super(parent, spliterator);
-            helper = parent.helper;
-        }
-
-        @Override
-        protected DoubleCollectorTask<P_IN> makeChild(Spliterator<P_IN> 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();
-        }
-
-        @Override
-        public void onCompletion(CountedCompleter caller) {
-            if (!isLeaf()) {
-                setLocalResult(new DoubleConcNode(leftChild.getLocalResult(), rightChild.getLocalResult()));
+        private static final class OfDouble<P_IN>
+                extends CollectorTask<P_IN, Double, Node.OfDouble, Node.Builder.OfDouble> {
+            OfDouble(PipelineHelper<Double> helper, Spliterator<P_IN> spliterator) {
+                super(helper, spliterator, Nodes::doubleBuilder, ConcNode.OfDouble::new);
             }
-            super.onCompletion(caller);
         }
     }
 }
--- a/jdk/src/share/classes/java/util/stream/SliceOps.java	Wed Jun 19 17:41:12 2013 -0700
+++ b/jdk/src/share/classes/java/util/stream/SliceOps.java	Thu Jun 20 10:45:46 2013 +0200
@@ -28,7 +28,10 @@
 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 @@
                     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 @@
             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);
-//            }
-//        }
-//    }
-
 }
--- a/jdk/test/java/util/stream/boottest/java/util/stream/DoubleNodeTest.java	Wed Jun 19 17:41:12 2013 -0700
+++ b/jdk/test/java/util/stream/boottest/java/util/stream/DoubleNodeTest.java	Thu Jun 20 10:45:46 2013 +0200
@@ -102,7 +102,7 @@
 
         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 @@
             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));
         }
--- a/jdk/test/java/util/stream/boottest/java/util/stream/IntNodeTest.java	Wed Jun 19 17:41:12 2013 -0700
+++ b/jdk/test/java/util/stream/boottest/java/util/stream/IntNodeTest.java	Thu Jun 20 10:45:46 2013 +0200
@@ -102,7 +102,7 @@
 
         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 @@
             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));
         }
--- a/jdk/test/java/util/stream/boottest/java/util/stream/LongNodeTest.java	Wed Jun 19 17:41:12 2013 -0700
+++ b/jdk/test/java/util/stream/boottest/java/util/stream/LongNodeTest.java	Thu Jun 20 10:45:46 2013 +0200
@@ -102,7 +102,7 @@
 
         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 @@
             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));
         }