changeset 7380:a50394c44464

8016251: Balanced spliterator for SpinedBuffer Reviewed-by: mduigou Contributed-by: Brian Goetz <brian.goetz@oracle.com>, Peter Levart <peter.levart@gmail.com>, Paul Sandoz <paul.sandoz@oracle.com>
author psandoz
date Thu, 13 Jun 2013 11:13:30 +0200
parents 3c7bab68cd2f
children f3609297a868
files src/share/classes/java/util/stream/DoublePipeline.java src/share/classes/java/util/stream/IntPipeline.java src/share/classes/java/util/stream/LongPipeline.java src/share/classes/java/util/stream/Node.java src/share/classes/java/util/stream/Nodes.java src/share/classes/java/util/stream/SortedOps.java src/share/classes/java/util/stream/SpinedBuffer.java test/java/util/stream/boottest/java/util/stream/DoubleNodeTest.java test/java/util/stream/boottest/java/util/stream/IntNodeTest.java test/java/util/stream/boottest/java/util/stream/LongNodeTest.java test/java/util/stream/boottest/java/util/stream/SpinedBufferTest.java
diffstat 11 files changed, 202 insertions(+), 123 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/util/stream/DoublePipeline.java	Wed Jun 12 23:54:55 2013 -0700
+++ b/src/share/classes/java/util/stream/DoublePipeline.java	Thu Jun 13 11:13:30 2013 +0200
@@ -461,7 +461,7 @@
     @Override
     public final double[] toArray() {
         return Nodes.flattenDouble((Node.OfDouble) evaluateToArrayNode(Double[]::new))
-                        .asDoubleArray();
+                        .asPrimitiveArray();
     }
 
     //
--- a/src/share/classes/java/util/stream/IntPipeline.java	Wed Jun 12 23:54:55 2013 -0700
+++ b/src/share/classes/java/util/stream/IntPipeline.java	Thu Jun 13 11:13:30 2013 +0200
@@ -498,7 +498,7 @@
     @Override
     public final int[] toArray() {
         return Nodes.flattenInt((Node.OfInt) evaluateToArrayNode(Integer[]::new))
-                        .asIntArray();
+                        .asPrimitiveArray();
     }
 
     //
--- a/src/share/classes/java/util/stream/LongPipeline.java	Wed Jun 12 23:54:55 2013 -0700
+++ b/src/share/classes/java/util/stream/LongPipeline.java	Thu Jun 13 11:13:30 2013 +0200
@@ -479,7 +479,8 @@
 
     @Override
     public final long[] toArray() {
-        return Nodes.flattenLong((Node.OfLong) evaluateToArrayNode(Long[]::new)).asLongArray();
+        return Nodes.flattenLong((Node.OfLong) evaluateToArrayNode(Long[]::new))
+                .asPrimitiveArray();
     }
 
 
--- a/src/share/classes/java/util/stream/Node.java	Wed Jun 12 23:54:55 2013 -0700
+++ b/src/share/classes/java/util/stream/Node.java	Thu Jun 13 11:13:30 2013 +0200
@@ -242,7 +242,7 @@
          * 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 #asIntArray()}.
+         * recommended to invoke {@link #asPrimitiveArray()}.
          */
         @Override
         default Integer[] asArray(IntFunction<Integer[]> generator) {
@@ -254,7 +254,7 @@
         /**
          * {@inheritDoc}
          *
-         * @implSpec the default implementation invokes {@link #asIntArray()} to
+         * @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)}.
@@ -264,7 +264,7 @@
             if (Tripwire.ENABLED)
                 Tripwire.trip(getClass(), "{0} calling Node.OfInt.copyInto(Integer[], int)");
 
-            int[] array = asIntArray();
+            int[] array = asPrimitiveArray();
             for (int i = 0; i < array.length; i++) {
                 boxed[offset + i] = array[i];
             }
@@ -285,7 +285,7 @@
          *
          * @return an array containing the contents of this {@code Node}
          */
-        int[] asIntArray();
+        int[] asPrimitiveArray();
 
         /**
          * Copies the content of this {@code Node} into an int[] array, starting
@@ -362,7 +362,7 @@
          * 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 #asLongArray()}.
+         * invoke {@link #asPrimitiveArray()}.
          */
         @Override
         default Long[] asArray(IntFunction<Long[]> generator) {
@@ -374,7 +374,7 @@
         /**
          * {@inheritDoc}
          *
-         * @implSpec the default implementation invokes {@link #asLongArray()}
+         * @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)}.
@@ -384,7 +384,7 @@
             if (Tripwire.ENABLED)
                 Tripwire.trip(getClass(), "{0} calling Node.OfInt.copyInto(Long[], int)");
 
-            long[] array = asLongArray();
+            long[] array = asPrimitiveArray();
             for (int i = 0; i < array.length; i++) {
                 boxed[offset + i] = array[i];
             }
@@ -405,7 +405,7 @@
          *
          * @return an array containing the contents of this {@code Node}
          */
-        long[] asLongArray();
+        long[] asPrimitiveArray();
 
         /**
          * Copies the content of this {@code Node} into a long[] array, starting
@@ -485,7 +485,7 @@
          * 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 #asDoubleArray()}.
+         * to invoke {@link #asPrimitiveArray()}.
          */
         @Override
         default Double[] asArray(IntFunction<Double[]> generator) {
@@ -497,7 +497,7 @@
         /**
          * {@inheritDoc}
          *
-         * @implSpec the default implementation invokes {@link #asDoubleArray()}
+         * @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)}.
@@ -507,7 +507,7 @@
             if (Tripwire.ENABLED)
                 Tripwire.trip(getClass(), "{0} calling Node.OfDouble.copyInto(Double[], int)");
 
-            double[] array = asDoubleArray();
+            double[] array = asPrimitiveArray();
             for (int i = 0; i < array.length; i++) {
                 boxed[offset + i] = array[i];
             }
@@ -528,7 +528,7 @@
          *
          * @return an array containing the contents of this {@code Node}
          */
-        double[] asDoubleArray();
+        double[] asPrimitiveArray();
 
         /**
          * Copies the content of this {@code Node} into a double[] array, starting
--- a/src/share/classes/java/util/stream/Nodes.java	Wed Jun 12 23:54:55 2013 -0700
+++ b/src/share/classes/java/util/stream/Nodes.java	Thu Jun 13 11:13:30 2013 +0200
@@ -679,7 +679,7 @@
             }
 
             @Override
-            public int[] asIntArray() {
+            public int[] asPrimitiveArray() {
                 return EMPTY_INT_ARRAY;
             }
         }
@@ -696,7 +696,7 @@
             }
 
             @Override
-            public long[] asLongArray() {
+            public long[] asPrimitiveArray() {
                 return EMPTY_LONG_ARRAY;
             }
         }
@@ -713,7 +713,7 @@
             }
 
             @Override
-            public double[] asDoubleArray() {
+            public double[] asPrimitiveArray() {
                 return EMPTY_DOUBLE_ARRAY;
             }
         }
@@ -1395,7 +1395,7 @@
         }
 
         @Override
-        public int[] asIntArray() {
+        public int[] asPrimitiveArray() {
             if (array.length == curSize) {
                 return array;
             } else {
@@ -1449,7 +1449,7 @@
         }
 
         @Override
-        public long[] asLongArray() {
+        public long[] asPrimitiveArray() {
             if (array.length == curSize) {
                 return array;
             } else {
@@ -1503,7 +1503,7 @@
         }
 
         @Override
-        public double[] asDoubleArray() {
+        public double[] asPrimitiveArray() {
             if (array.length == curSize) {
                 return array;
             } else {
@@ -1561,7 +1561,7 @@
         }
 
         @Override
-        public int[] asIntArray() {
+        public int[] asPrimitiveArray() {
             int[] array = new int[(int) count()];
             copyInto(array, 0);
             return array;
@@ -1594,7 +1594,7 @@
         }
 
         @Override
-        public long[] asLongArray() {
+        public long[] asPrimitiveArray() {
             long[] array = new long[(int) count()];
             copyInto(array, 0);
             return array;
@@ -1627,7 +1627,7 @@
         }
 
         @Override
-        public double[] asDoubleArray() {
+        public double[] asPrimitiveArray() {
             double[] array = new double[(int) count()];
             copyInto(array, 0);
             return array;
@@ -1844,9 +1844,9 @@
         }
 
         @Override
-        public int[] asIntArray() {
+        public int[] asPrimitiveArray() {
             assert !building : "during building";
-            return super.asIntArray();
+            return super.asPrimitiveArray();
         }
 
         @Override
@@ -1904,9 +1904,9 @@
         }
 
         @Override
-        public long[] asLongArray() {
+        public long[] asPrimitiveArray() {
             assert !building : "during building";
-            return super.asLongArray();
+            return super.asPrimitiveArray();
         }
 
         @Override
@@ -1964,9 +1964,9 @@
         }
 
         @Override
-        public double[] asDoubleArray() {
+        public double[] asPrimitiveArray() {
             assert !building : "during building";
-            return super.asDoubleArray();
+            return super.asPrimitiveArray();
         }
 
         @Override
--- a/src/share/classes/java/util/stream/SortedOps.java	Wed Jun 12 23:54:55 2013 -0700
+++ b/src/share/classes/java/util/stream/SortedOps.java	Thu Jun 13 11:13:30 2013 +0200
@@ -192,7 +192,7 @@
             else {
                 Node.OfInt n = (Node.OfInt) helper.evaluate(spliterator, true, generator);
 
-                int[] content = n.asIntArray();
+                int[] content = n.asPrimitiveArray();
                 Arrays.parallelSort(content);
 
                 return Nodes.node(content);
@@ -231,7 +231,7 @@
             else {
                 Node.OfLong n = (Node.OfLong) helper.evaluate(spliterator, true, generator);
 
-                long[] content = n.asLongArray();
+                long[] content = n.asPrimitiveArray();
                 Arrays.parallelSort(content);
 
                 return Nodes.node(content);
@@ -270,7 +270,7 @@
             else {
                 Node.OfDouble n = (Node.OfDouble) helper.evaluate(spliterator, true, generator);
 
-                double[] content = n.asDoubleArray();
+                double[] content = n.asPrimitiveArray();
                 Arrays.parallelSort(content);
 
                 return Nodes.node(content);
@@ -401,7 +401,7 @@
 
         @Override
         public void end() {
-            int[] ints = b.asIntArray();
+            int[] ints = b.asPrimitiveArray();
             Arrays.sort(ints);
             downstream.begin(ints.length);
             for (int anInt : ints)
@@ -466,7 +466,7 @@
 
         @Override
         public void end() {
-            long[] longs = b.asLongArray();
+            long[] longs = b.asPrimitiveArray();
             Arrays.sort(longs);
             downstream.begin(longs.length);
             for (long aLong : longs)
@@ -531,7 +531,7 @@
 
         @Override
         public void end() {
-            double[] doubles = b.asDoubleArray();
+            double[] doubles = b.asPrimitiveArray();
             Arrays.sort(doubles);
             downstream.begin(doubles.length);
             for (double aDouble : doubles)
--- a/src/share/classes/java/util/stream/SpinedBuffer.java	Wed Jun 12 23:54:55 2013 -0700
+++ b/src/share/classes/java/util/stream/SpinedBuffer.java	Thu Jun 13 11:13:30 2013 +0200
@@ -269,25 +269,45 @@
      * Return a {@link Spliterator} describing the contents of the buffer.
      */
     public Spliterator<E> spliterator() {
-        return new Spliterator<E>() {
+        class Splitr implements Spliterator<E> {
             // The current spine index
             int splSpineIndex;
 
+            // Last spine index
+            final int lastSpineIndex;
+
             // The current element index into the current spine
             int splElementIndex;
 
-            // When splSpineIndex >= spineIndex and splElementIndex >= elementIndex then
+            // Last spine's last element index + 1
+            final int lastSpineElementFence;
+
+            // When splSpineIndex >= lastSpineIndex and
+            // splElementIndex >= lastSpineElementFence then
             // this spliterator is fully traversed
             // tryAdvance can set splSpineIndex > spineIndex if the last spine is full
 
             // The current spine array
-            E[] splChunk = (spine == null) ? curChunk : spine[0];
+            E[] splChunk;
+
+            Splitr(int firstSpineIndex, int lastSpineIndex,
+                   int firstSpineElementIndex, int lastSpineElementFence) {
+                this.splSpineIndex = firstSpineIndex;
+                this.lastSpineIndex = lastSpineIndex;
+                this.splElementIndex = firstSpineElementIndex;
+                this.lastSpineElementFence = lastSpineElementFence;
+                assert spine != null || firstSpineIndex == 0 && lastSpineIndex == 0;
+                splChunk = (spine == null) ? curChunk : spine[firstSpineIndex];
+            }
 
             @Override
             public long estimateSize() {
-                return (spine == null)
-                       ? (elementIndex - splElementIndex)
-                       : count() - (priorElementCount[splSpineIndex] + splElementIndex);
+                return (splSpineIndex == lastSpineIndex)
+                       ? (long) lastSpineElementFence - splElementIndex
+                       : // # of elements prior to end -
+                       priorElementCount[lastSpineIndex] + lastSpineElementFence -
+                       // # of elements prior to current
+                       priorElementCount[splSpineIndex] - splElementIndex;
             }
 
             @Override
@@ -297,14 +317,14 @@
 
             @Override
             public boolean tryAdvance(Consumer<? super E> consumer) {
-                if (splSpineIndex < spineIndex
-                    || (splSpineIndex == spineIndex && splElementIndex < elementIndex)) {
+                if (splSpineIndex < lastSpineIndex
+                    || (splSpineIndex == lastSpineIndex && splElementIndex < lastSpineElementFence)) {
                     consumer.accept(splChunk[splElementIndex++]);
 
                     if (splElementIndex == splChunk.length) {
                         splElementIndex = 0;
                         ++splSpineIndex;
-                        if (spine != null && splSpineIndex < spine.length)
+                        if (spine != null && splSpineIndex <= lastSpineIndex)
                             splChunk = spine[splSpineIndex];
                     }
                     return true;
@@ -314,45 +334,47 @@
 
             @Override
             public void forEachRemaining(Consumer<? super E> consumer) {
-                if (splSpineIndex < spineIndex
-                    || (splSpineIndex == spineIndex && splElementIndex < elementIndex)) {
+                if (splSpineIndex < lastSpineIndex
+                    || (splSpineIndex == lastSpineIndex && splElementIndex < lastSpineElementFence)) {
                     int i = splElementIndex;
                     // completed chunks, if any
-                    for (int sp = splSpineIndex; sp < spineIndex; sp++) {
+                    for (int sp = splSpineIndex; sp < lastSpineIndex; sp++) {
                         E[] chunk = spine[sp];
                         for (; i < chunk.length; i++) {
                             consumer.accept(chunk[i]);
                         }
                         i = 0;
                     }
-
-                    // current chunk
-                    E[] chunk = curChunk;
-                    int hElementIndex = elementIndex;
+                    // last (or current uncompleted) chunk
+                    E[] chunk = (splSpineIndex == lastSpineIndex) ? splChunk : spine[lastSpineIndex];
+                    int hElementIndex = lastSpineElementFence;
                     for (; i < hElementIndex; i++) {
                         consumer.accept(chunk[i]);
                     }
-
-                    splSpineIndex = spineIndex;
-                    splElementIndex = elementIndex;
+                    // mark consumed
+                    splSpineIndex = lastSpineIndex;
+                    splElementIndex = lastSpineElementFence;
                 }
             }
 
             @Override
             public Spliterator<E> trySplit() {
-                if (splSpineIndex < spineIndex) {
-                    Spliterator<E> ret = Arrays.spliterator(spine[splSpineIndex],
-                                                            splElementIndex, spine[splSpineIndex].length);
-                    splChunk = spine[++splSpineIndex];
+                if (splSpineIndex < lastSpineIndex) {
+                    // split just before last chunk (if it is full this means 50:50 split)
+                    Spliterator<E> ret = new Splitr(splSpineIndex, lastSpineIndex - 1,
+                                                    splElementIndex, spine[lastSpineIndex-1].length);
+                    // position to start of last chunk
+                    splSpineIndex = lastSpineIndex;
                     splElementIndex = 0;
+                    splChunk = spine[splSpineIndex];
                     return ret;
                 }
-                else if (splSpineIndex == spineIndex) {
-                    int t = (elementIndex - splElementIndex) / 2;
+                else if (splSpineIndex == lastSpineIndex) {
+                    int t = (lastSpineElementFence - splElementIndex) / 2;
                     if (t == 0)
                         return null;
                     else {
-                        Spliterator<E> ret = Arrays.spliterator(curChunk, splElementIndex, splElementIndex + t);
+                        Spliterator<E> ret = Arrays.spliterator(splChunk, splElementIndex, splElementIndex + t);
                         splElementIndex += t;
                         return ret;
                     }
@@ -361,7 +383,8 @@
                     return null;
                 }
             }
-        };
+        }
+        return new Splitr(0, spineIndex, 0, elementIndex);
     }
 
     /**
@@ -436,7 +459,7 @@
         protected abstract T_ARR[] newArrayArray(int size);
 
         /** Create a new array of the proper type and size */
-        protected abstract T_ARR newArray(int size);
+        public abstract T_ARR newArray(int size);
 
         /** Get the length of an array */
         protected abstract int arrayLength(T_ARR array);
@@ -555,30 +578,53 @@
             arrayForEach(curChunk, 0, elementIndex, consumer);
         }
 
-        abstract class BaseSpliterator<T_SPLITER extends Spliterator<E>>
-                implements Spliterator<E> {
+        abstract class BaseSpliterator<T_SPLITR extends Spliterator.OfPrimitive<E, T_CONS, T_SPLITR>>
+                implements Spliterator.OfPrimitive<E, T_CONS, T_SPLITR> {
             // The current spine index
             int splSpineIndex;
 
+            // Last spine index
+            final int lastSpineIndex;
+
             // The current element index into the current spine
             int splElementIndex;
 
-            // When splSpineIndex >= spineIndex and splElementIndex >= elementIndex then
+            // Last spine's last element index + 1
+            final int lastSpineElementFence;
+
+            // When splSpineIndex >= lastSpineIndex and
+            // splElementIndex >= lastSpineElementFence then
             // this spliterator is fully traversed
             // tryAdvance can set splSpineIndex > spineIndex if the last spine is full
 
             // The current spine array
-            T_ARR splChunk = (spine == null) ? curChunk : spine[0];
+            T_ARR splChunk;
+
+            BaseSpliterator(int firstSpineIndex, int lastSpineIndex,
+                            int firstSpineElementIndex, int lastSpineElementFence) {
+                this.splSpineIndex = firstSpineIndex;
+                this.lastSpineIndex = lastSpineIndex;
+                this.splElementIndex = firstSpineElementIndex;
+                this.lastSpineElementFence = lastSpineElementFence;
+                assert spine != null || firstSpineIndex == 0 && lastSpineIndex == 0;
+                splChunk = (spine == null) ? curChunk : spine[firstSpineIndex];
+            }
+
+            abstract T_SPLITR newSpliterator(int firstSpineIndex, int lastSpineIndex,
+                                             int firstSpineElementIndex, int lastSpineElementFence);
 
             abstract void arrayForOne(T_ARR array, int index, T_CONS consumer);
 
-            abstract T_SPLITER arraySpliterator(T_ARR array, int offset, int len);
+            abstract T_SPLITR arraySpliterator(T_ARR array, int offset, int len);
 
             @Override
             public long estimateSize() {
-                return (spine == null)
-                       ? (elementIndex - splElementIndex)
-                       : count() - (priorElementCount[splSpineIndex] + splElementIndex);
+                return (splSpineIndex == lastSpineIndex)
+                       ? (long) lastSpineElementFence - splElementIndex
+                       : // # of elements prior to end -
+                       priorElementCount[lastSpineIndex] + lastSpineElementFence -
+                       // # of elements prior to current
+                       priorElementCount[splSpineIndex] - splElementIndex;
             }
 
             @Override
@@ -586,15 +632,16 @@
                 return SPLITERATOR_CHARACTERISTICS;
             }
 
+            @Override
             public boolean tryAdvance(T_CONS consumer) {
-                if (splSpineIndex < spineIndex
-                    || (splSpineIndex == spineIndex && splElementIndex < elementIndex)) {
+                if (splSpineIndex < lastSpineIndex
+                    || (splSpineIndex == lastSpineIndex && splElementIndex < lastSpineElementFence)) {
                     arrayForOne(splChunk, splElementIndex++, consumer);
 
                     if (splElementIndex == arrayLength(splChunk)) {
                         splElementIndex = 0;
                         ++splSpineIndex;
-                        if (spine != null && splSpineIndex < spine.length)
+                        if (spine != null && splSpineIndex <= lastSpineIndex)
                             splChunk = spine[splSpineIndex];
                     }
                     return true;
@@ -602,39 +649,44 @@
                 return false;
             }
 
+            @Override
             public void forEachRemaining(T_CONS consumer) {
-                if (splSpineIndex < spineIndex
-                    || (splSpineIndex == spineIndex && splElementIndex < elementIndex)) {
+                if (splSpineIndex < lastSpineIndex
+                    || (splSpineIndex == lastSpineIndex && splElementIndex < lastSpineElementFence)) {
                     int i = splElementIndex;
                     // completed chunks, if any
-                    for (int sp = splSpineIndex; sp < spineIndex; sp++) {
+                    for (int sp = splSpineIndex; sp < lastSpineIndex; sp++) {
                         T_ARR chunk = spine[sp];
                         arrayForEach(chunk, i, arrayLength(chunk), consumer);
                         i = 0;
                     }
-
-                    arrayForEach(curChunk, i, elementIndex, consumer);
-
-                    splSpineIndex = spineIndex;
-                    splElementIndex = elementIndex;
+                    // last (or current uncompleted) chunk
+                    T_ARR chunk = (splSpineIndex == lastSpineIndex) ? splChunk : spine[lastSpineIndex];
+                    arrayForEach(chunk, i, lastSpineElementFence, consumer);
+                    // mark consumed
+                    splSpineIndex = lastSpineIndex;
+                    splElementIndex = lastSpineElementFence;
                 }
             }
 
             @Override
-            public T_SPLITER trySplit() {
-                if (splSpineIndex < spineIndex) {
-                    T_SPLITER ret = arraySpliterator(spine[splSpineIndex], splElementIndex,
-                                                     arrayLength(spine[splSpineIndex]) - splElementIndex);
-                    splChunk = spine[++splSpineIndex];
+            public T_SPLITR trySplit() {
+                if (splSpineIndex < lastSpineIndex) {
+                    // split just before last chunk (if it is full this means 50:50 split)
+                    T_SPLITR ret = newSpliterator(splSpineIndex, lastSpineIndex - 1,
+                                                  splElementIndex, arrayLength(spine[lastSpineIndex - 1]));
+                    // position us to start of last chunk
+                    splSpineIndex = lastSpineIndex;
                     splElementIndex = 0;
+                    splChunk = spine[splSpineIndex];
                     return ret;
                 }
-                else if (splSpineIndex == spineIndex) {
-                    int t = (elementIndex - splElementIndex) / 2;
+                else if (splSpineIndex == lastSpineIndex) {
+                    int t = (lastSpineElementFence - splElementIndex) / 2;
                     if (t == 0)
                         return null;
                     else {
-                        T_SPLITER ret = arraySpliterator(curChunk, splElementIndex, t);
+                        T_SPLITR ret = arraySpliterator(splChunk, splElementIndex, t);
                         splElementIndex += t;
                         return ret;
                     }
@@ -675,7 +727,7 @@
         }
 
         @Override
-        protected int[] newArray(int size) {
+        public int[] newArray(int size) {
             return new int[size];
         }
 
@@ -706,10 +758,6 @@
                 return spine[ch][(int) (index-priorElementCount[ch])];
         }
 
-        public int[] asIntArray() {
-            return asPrimitiveArray();
-        }
-
         @Override
         public PrimitiveIterator.OfInt iterator() {
             return Spliterators.iterator(spliterator());
@@ -718,6 +766,18 @@
         public Spliterator.OfInt spliterator() {
             class Splitr extends BaseSpliterator<Spliterator.OfInt>
                     implements Spliterator.OfInt {
+                Splitr(int firstSpineIndex, int lastSpineIndex,
+                       int firstSpineElementIndex, int lastSpineElementFence) {
+                    super(firstSpineIndex, lastSpineIndex,
+                          firstSpineElementIndex, lastSpineElementFence);
+                }
+
+                @Override
+                Splitr newSpliterator(int firstSpineIndex, int lastSpineIndex,
+                                      int firstSpineElementIndex, int lastSpineElementFence) {
+                    return new Splitr(firstSpineIndex, lastSpineIndex,
+                                      firstSpineElementIndex, lastSpineElementFence);
+                }
 
                 @Override
                 void arrayForOne(int[] array, int index, IntConsumer consumer) {
@@ -728,13 +788,13 @@
                 Spliterator.OfInt arraySpliterator(int[] array, int offset, int len) {
                     return Arrays.spliterator(array, offset, offset+len);
                 }
-            };
-            return new Splitr();
+            }
+            return new Splitr(0, spineIndex, 0, elementIndex);
         }
 
         @Override
         public String toString() {
-            int[] array = asIntArray();
+            int[] array = asPrimitiveArray();
             if (array.length < 200) {
                 return String.format("%s[length=%d, chunks=%d]%s",
                                      getClass().getSimpleName(), array.length,
@@ -778,7 +838,7 @@
         }
 
         @Override
-        protected long[] newArray(int size) {
+        public long[] newArray(int size) {
             return new long[size];
         }
 
@@ -809,10 +869,6 @@
                 return spine[ch][(int) (index-priorElementCount[ch])];
         }
 
-        public long[] asLongArray() {
-            return asPrimitiveArray();
-        }
-
         @Override
         public PrimitiveIterator.OfLong iterator() {
             return Spliterators.iterator(spliterator());
@@ -822,6 +878,19 @@
         public Spliterator.OfLong spliterator() {
             class Splitr extends BaseSpliterator<Spliterator.OfLong>
                     implements Spliterator.OfLong {
+                Splitr(int firstSpineIndex, int lastSpineIndex,
+                       int firstSpineElementIndex, int lastSpineElementFence) {
+                    super(firstSpineIndex, lastSpineIndex,
+                          firstSpineElementIndex, lastSpineElementFence);
+                }
+
+                @Override
+                Splitr newSpliterator(int firstSpineIndex, int lastSpineIndex,
+                                      int firstSpineElementIndex, int lastSpineElementFence) {
+                    return new Splitr(firstSpineIndex, lastSpineIndex,
+                                      firstSpineElementIndex, lastSpineElementFence);
+                }
+
                 @Override
                 void arrayForOne(long[] array, int index, LongConsumer consumer) {
                     consumer.accept(array[index]);
@@ -831,13 +900,13 @@
                 Spliterator.OfLong arraySpliterator(long[] array, int offset, int len) {
                     return Arrays.spliterator(array, offset, offset+len);
                 }
-            };
-            return new Splitr();
+            }
+            return new Splitr(0, spineIndex, 0, elementIndex);
         }
 
         @Override
         public String toString() {
-            long[] array = asLongArray();
+            long[] array = asPrimitiveArray();
             if (array.length < 200) {
                 return String.format("%s[length=%d, chunks=%d]%s",
                                      getClass().getSimpleName(), array.length,
@@ -882,7 +951,7 @@
         }
 
         @Override
-        protected double[] newArray(int size) {
+        public double[] newArray(int size) {
             return new double[size];
         }
 
@@ -913,10 +982,6 @@
                 return spine[ch][(int) (index-priorElementCount[ch])];
         }
 
-        public double[] asDoubleArray() {
-            return asPrimitiveArray();
-        }
-
         @Override
         public PrimitiveIterator.OfDouble iterator() {
             return Spliterators.iterator(spliterator());
@@ -925,6 +990,19 @@
         public Spliterator.OfDouble spliterator() {
             class Splitr extends BaseSpliterator<Spliterator.OfDouble>
                     implements Spliterator.OfDouble {
+                Splitr(int firstSpineIndex, int lastSpineIndex,
+                       int firstSpineElementIndex, int lastSpineElementFence) {
+                    super(firstSpineIndex, lastSpineIndex,
+                          firstSpineElementIndex, lastSpineElementFence);
+                }
+
+                @Override
+                Splitr newSpliterator(int firstSpineIndex, int lastSpineIndex,
+                                      int firstSpineElementIndex, int lastSpineElementFence) {
+                    return new Splitr(firstSpineIndex, lastSpineIndex,
+                                      firstSpineElementIndex, lastSpineElementFence);
+                }
+
                 @Override
                 void arrayForOne(double[] array, int index, DoubleConsumer consumer) {
                     consumer.accept(array[index]);
@@ -935,12 +1013,12 @@
                     return Arrays.spliterator(array, offset, offset+len);
                 }
             }
-            return new Splitr();
+            return new Splitr(0, spineIndex, 0, elementIndex);
         }
 
         @Override
         public String toString() {
-            double[] array = asDoubleArray();
+            double[] array = asPrimitiveArray();
             if (array.length < 200) {
                 return String.format("%s[length=%d, chunks=%d]%s",
                                      getClass().getSimpleName(), array.length,
--- a/test/java/util/stream/boottest/java/util/stream/DoubleNodeTest.java	Wed Jun 12 23:54:55 2013 -0700
+++ b/test/java/util/stream/boottest/java/util/stream/DoubleNodeTest.java	Thu Jun 13 11:13:30 2013 +0200
@@ -122,12 +122,12 @@
 
     @Test(dataProvider = "nodes")
     public void testAsArray(double[] array, Node.OfDouble n) {
-        assertEquals(n.asDoubleArray(), array);
+        assertEquals(n.asPrimitiveArray(), array);
     }
 
     @Test(dataProvider = "nodes")
     public void testFlattenAsArray(double[] array, Node.OfDouble n) {
-        assertEquals(Nodes.flattenDouble(n).asDoubleArray(), array);
+        assertEquals(Nodes.flattenDouble(n).asPrimitiveArray(), array);
     }
 
     @Test(dataProvider = "nodes")
--- a/test/java/util/stream/boottest/java/util/stream/IntNodeTest.java	Wed Jun 12 23:54:55 2013 -0700
+++ b/test/java/util/stream/boottest/java/util/stream/IntNodeTest.java	Thu Jun 13 11:13:30 2013 +0200
@@ -122,12 +122,12 @@
 
     @Test(dataProvider = "nodes")
     public void testAsArray(int[] array, Node.OfInt n) {
-        assertEquals(n.asIntArray(), array);
+        assertEquals(n.asPrimitiveArray(), array);
     }
 
     @Test(dataProvider = "nodes")
     public void testFlattenAsArray(int[] array, Node.OfInt n) {
-        assertEquals(Nodes.flattenInt(n).asIntArray(), array);
+        assertEquals(Nodes.flattenInt(n).asPrimitiveArray(), array);
     }
 
     @Test(dataProvider = "nodes")
--- a/test/java/util/stream/boottest/java/util/stream/LongNodeTest.java	Wed Jun 12 23:54:55 2013 -0700
+++ b/test/java/util/stream/boottest/java/util/stream/LongNodeTest.java	Thu Jun 13 11:13:30 2013 +0200
@@ -122,12 +122,12 @@
 
     @Test(dataProvider = "nodes")
     public void testAsArray(long[] array, Node.OfLong n) {
-        assertEquals(n.asLongArray(), array);
+        assertEquals(n.asPrimitiveArray(), array);
     }
 
     @Test(dataProvider = "nodes")
     public void testFlattenAsArray(long[] array, Node.OfLong n) {
-        assertEquals(Nodes.flattenLong(n).asLongArray(), array);
+        assertEquals(Nodes.flattenLong(n).asPrimitiveArray(), array);
     }
 
     @Test(dataProvider = "nodes")
--- a/test/java/util/stream/boottest/java/util/stream/SpinedBufferTest.java	Wed Jun 12 23:54:55 2013 -0700
+++ b/test/java/util/stream/boottest/java/util/stream/SpinedBufferTest.java	Thu Jun 13 11:13:30 2013 +0200
@@ -210,7 +210,7 @@
         list2.clear();
         sb.forEach((int i) -> list2.add(i));
         assertEquals(list1, list2);
-        int[] array = sb.asIntArray();
+        int[] array = sb.asPrimitiveArray();
         list2.clear();
         for (int i : array)
             list2.add(i);
@@ -285,7 +285,7 @@
         list2.clear();
         sb.forEach((long i) -> list2.add(i));
         assertEquals(list1, list2);
-        long[] array = sb.asLongArray();
+        long[] array = sb.asPrimitiveArray();
         list2.clear();
         for (long i : array)
             list2.add(i);
@@ -361,7 +361,7 @@
         list2.clear();
         sb.forEach((double i) -> list2.add(i));
         assertEquals(list1, list2);
-        double[] array = sb.asDoubleArray();
+        double[] array = sb.asPrimitiveArray();
         list2.clear();
         for (double i : array)
             list2.add(i);