changeset 6424:3039509789a6

Clean up Streams API for constructing streams; move Array-related helpers (iterator, spliterator) into Arrays
author briangoetz
date Fri, 16 Nov 2012 15:18:25 -0500
parents ba27872c81f8
children c79261933eb2
files src/share/classes/java/io/BufferedReader.java src/share/classes/java/lang/CharSequence.java src/share/classes/java/util/ArrayList.java src/share/classes/java/util/Arrays.java src/share/classes/java/util/Collection.java src/share/classes/java/util/LinkedHashSet.java src/share/classes/java/util/List.java src/share/classes/java/util/Optional.java src/share/classes/java/util/Set.java src/share/classes/java/util/SortedSet.java src/share/classes/java/util/Vector.java src/share/classes/java/util/logging/Logger.java src/share/classes/java/util/stream/Spliterator.java src/share/classes/java/util/stream/Streams.java src/share/classes/java/util/stream/op/Nodes.java test-ng/tests/org/openjdk/tests/java/util/FillableStringTest.java test-ng/tests/org/openjdk/tests/java/util/stream/op/MapOpTest.java test-ng/tests/org/openjdk/tests/java/util/stream/op/ToArrayOpTest.java
diffstat 18 files changed, 239 insertions(+), 245 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/io/BufferedReader.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/src/share/classes/java/io/BufferedReader.java	Fri Nov 16 15:18:25 2012 -0500
@@ -579,7 +579,7 @@
                             throw new NoSuchElementException();
                     }
                 };
-                linesStream = Streams.stream(iter, StreamOpFlags.IS_ORDERED);
+                linesStream = Streams.stream(Streams.sequentialSpliterator(iter), StreamOpFlags.IS_ORDERED);
             }
             return linesStream;
         }
--- a/src/share/classes/java/lang/CharSequence.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/src/share/classes/java/lang/CharSequence.java	Fri Nov 16 15:18:25 2012 -0500
@@ -122,7 +122,8 @@
      * @return an Iterable of Character values from this sequence
      */
     public default Stream<Character> asChars() {
-        return Streams.stream(new CharacterIterable(this), StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED);
+        return Streams.stream(Streams.sequentialSpliterator(new CharacterIterable(this)),
+                              StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED);
     }
 
     /**
@@ -141,7 +142,8 @@
      * @return an Iterable of Unicode code points from this sequence
      */
     public default Stream<Integer> asCodePoints() {
-        return Streams.stream(new CodePointIterable(this), StreamOpFlags.IS_ORDERED);
+        return Streams.stream(Streams.sequentialSpliterator(new CodePointIterable(this)),
+                              StreamOpFlags.IS_ORDERED);
     }
 }
 
--- a/src/share/classes/java/util/ArrayList.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/src/share/classes/java/util/ArrayList.java	Fri Nov 16 15:18:25 2012 -0500
@@ -1135,13 +1135,13 @@
 
     @Override
     public Stream<E> stream() {
-        return Streams.stream(() -> Streams.spliterator((E[]) elementData, 0, size),
+        return Streams.stream(() -> Arrays.spliterator((E[]) elementData, 0, size),
                               StreamOpFlags.IS_ORDERED | StreamOpFlags.IS_SIZED);
     }
 
     @Override
     public Stream<E> parallel() {
-        return Streams.parallel(() -> Streams.spliterator((E[]) elementData, 0, size),
+        return Streams.parallel(() -> Arrays.spliterator((E[]) elementData, 0, size),
                                 StreamOpFlags.IS_ORDERED | StreamOpFlags.IS_SIZED);
     }
 }
--- a/src/share/classes/java/util/Arrays.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/src/share/classes/java/util/Arrays.java	Fri Nov 16 15:18:25 2012 -0500
@@ -26,6 +26,7 @@
 package java.util;
 
 import java.lang.reflect.*;
+import java.util.function.Block;
 import java.util.logging.Logger;
 import java.util.stream.*;
 
@@ -2908,12 +2909,12 @@
 
         @Override
         public Stream<E> stream() {
-            return Streams.stream(a);
+            return Arrays.stream(a);
         }
 
         @Override
         public Stream<E> parallel() {
-            return Streams.parallel(a);
+            return Arrays.parallel(a);
         }
     }
 
@@ -3699,6 +3700,105 @@
     }
 
 
+    public static<T> void checkBounds(T[] array, int offset, int length) throws IndexOutOfBoundsException {
+        if (offset < 0 || length < 0 || (offset + length) > array.length) {
+            throw new IndexOutOfBoundsException();
+        }
+    }
+
+    private static class ArrayIterator<T> implements Iterator<T> {
+        protected final T[] elements;
+        protected final int endOffset;
+        protected int curOffset;
+
+        private ArrayIterator(T[] elements) {
+            this(elements, 0, elements.length);
+        }
+
+        private ArrayIterator(T[] elements, int startOffset, int len) {
+            this.elements = Objects.requireNonNull(elements);
+            this.endOffset = startOffset + len;
+            this.curOffset = startOffset;
+
+            assert curOffset >= 0 : "offset not positive";
+            assert endOffset >= curOffset : "end lower than start";
+            assert (curOffset < elements.length) || (0 == len && 0 == curOffset) : "offset not in array";
+            assert endOffset <= elements.length : "end not in array";
+        }
+
+        public T next() {
+            if (!hasNext()) {
+                throw new NoSuchElementException();
+            }
+
+            return elements[curOffset++];
+        }
+
+        @Override
+        public final boolean hasNext() {
+            return curOffset < endOffset;
+        }
+    }
+
+    private static class ArraySpliterator<T> extends ArrayIterator<T> implements Spliterator<T> {
+        boolean traversing = false;
+
+        ArraySpliterator(T[] elements) {
+            this(elements, 0, elements.length);
+        }
+
+        ArraySpliterator(T[] elements, int offset, int length) {
+            super(elements, offset, length);
+            Arrays.checkBounds(elements, offset, length);
+        }
+
+        @Override
+        public int getSizeIfKnown() {
+            return endOffset - curOffset;
+        }
+
+        @Override
+        public int estimateSize() {
+            return getSizeIfKnown();
+        }
+
+        @Override
+        public boolean isPredictableSplits() {
+            return true;
+        }
+
+        @Override
+        public void forEach(Block<? super T> block) {
+            traversing = true;
+            for (int i= curOffset; i<endOffset; i++) {
+                block.apply(elements[i]);
+            }
+            // update only once; reduce heap write traffic
+            curOffset = endOffset;
+        }
+
+        @Override
+        public int getNaturalSplits() {
+            return (endOffset - curOffset > 1) ? 1 : 0;
+        }
+
+        @Override
+        public Spliterator<T> split() {
+            if (traversing) {
+                throw new IllegalStateException("split after starting traversal");
+            }
+            int t = (endOffset - curOffset) / 2;
+            Spliterator<T> ret = new ArraySpliterator<>(elements, curOffset, t);
+            curOffset += t;
+            return ret;
+        }
+
+        @Override
+        public Iterator<T> iterator() {
+            traversing = true;
+            return this;
+        }
+    }
 
     private static class ArraySink<T> implements Sink.OfValue<T> {
         private final T[] array;
@@ -3714,15 +3814,14 @@
             this.array = Objects.requireNonNull(array);
             this.offset = offset;
             this.length = length;
+            checkBounds(array, offset, length);
         }
 
         @Override
         public void begin(int size) {
+            if(size > length)
+                throw new IllegalStateException("size passed to ArraySink.begin exceeds array length");
             index = 0;
-            if(size > length) {
-                Logger.getLogger(Arrays.class.getName()).warning(
-                        "Estimate greater than length. There might be blood.");
-            }
         }
 
         @Override
@@ -3735,62 +3834,45 @@
     }
 
     public static<T> Iterator<T> iterator(T[] array) {
-        return iterable(array).iterator();
+        return iterator(array, 0, array.length);
     }
 
     public static<T> Iterator<T> iterator(T[] array, int offset, int length) {
-        return iterable(array,offset,length).iterator();
+        return new ArrayIterator<>(array, offset, length);
     }
 
     public static<T> Iterable<T> iterable(T[] array) {
-        return asList(array);
+        return iterable(array, 0, array.length);
     }
 
     public static<T> Iterable<T> iterable(T[] array, int offset, int length) {
-        // @@@ Perhaps just eliminate this method?
-        return asList(array).subList(offset, offset + length);
+        return () -> iterator(array, offset, length);
     }
 
     public static <T> Stream<T> stream(T[] array) {
-        return Streams.stream(array);
+        return stream(array, 0, array.length);
     }
 
-    public static <T> Stream<T> stream(T[] array, int offset, int length) {
-        return Streams.stream(array, offset, length);
+    public static <T> Stream<T> stream(T[] array, int offset, int length) throws IndexOutOfBoundsException {
+        return Streams.stream(spliterator(array, offset, length),
+                              StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED);
     }
 
     public static <T> Spliterator<T> spliterator(T[] array) {
-        return Streams.spliterator(array);
+        return spliterator(array, 0, array.length);
     }
 
-    public static <T> Spliterator<T> spliterator(T[] array, int offset, int length) {
-        return Streams.spliterator(array, offset, length);
+    public static <T> Spliterator<T> spliterator(T[] array, int offset, int length) throws IndexOutOfBoundsException {
+        return new ArraySpliterator<>(array, offset, length);
     }
 
     public static <T> Stream<T> parallel(T[] array) {
-        return Streams.parallel(array);
+        return parallel(array, 0, array.length);
     }
 
-    public static <T> Stream<T> parallel(T[] array, int offset, int length) {
-        return Streams.parallel(array, offset, length );
-    }
-
-    public static<T> Streamable<Stream<T>> asStreamable(T[] array) {
-        return asStreamable(array, 0, array.length);
-    }
-
-    public static<T> Streamable<Stream<T>> asStreamable(final T[] array, final int offset, final int length) {
-        return new Streamable<Stream<T>>() {
-            @Override
-            public Stream<T> stream() {
-                return Arrays.stream(array, offset, length);
-            }
-
-            @Override
-            public Stream<T> parallel() {
-                return Arrays.parallel(array, offset, length);
-            }
-        };
+    public static <T> Stream<T> parallel(T[] array, int offset, int length) throws IndexOutOfBoundsException {
+        return Streams.parallel(spliterator(array, offset, length),
+                              StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED);
     }
 
     public static<T> Sink<T> sink(T[] array) {
@@ -3800,9 +3882,4 @@
     public static<T> Sink<T> sink(T[] array, int offset, int length) {
         return new ArraySink<>(array, offset, length);
     }
-
-    @SafeVarargs
-    public static <T> Stream<T> asStream(T... a) {
-        return Streams.stream(a);
-    }
 }
--- a/src/share/classes/java/util/Collection.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/src/share/classes/java/util/Collection.java	Fri Nov 16 15:18:25 2012 -0500
@@ -492,7 +492,8 @@
 
     @Override
     default Stream<E> stream() {
-        return Streams.stream(this, StreamOpFlags.IS_SIZED);
+        return Streams.stream(Streams.sequentialSpliterator(this),
+                              StreamOpFlags.IS_SIZED);
     }
 
     @Override
--- a/src/share/classes/java/util/LinkedHashSet.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/src/share/classes/java/util/LinkedHashSet.java	Fri Nov 16 15:18:25 2012 -0500
@@ -175,6 +175,7 @@
 
     @Override
     public Stream<E> stream() {
-        return Streams.stream(this, StreamOpFlags.IS_SIZED | StreamOpFlags.IS_DISTINCT | StreamOpFlags.IS_ORDERED);
+        return Streams.stream(Streams.sequentialSpliterator(this),
+                              StreamOpFlags.IS_SIZED | StreamOpFlags.IS_DISTINCT | StreamOpFlags.IS_ORDERED);
     }
 }
--- a/src/share/classes/java/util/List.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/src/share/classes/java/util/List.java	Fri Nov 16 15:18:25 2012 -0500
@@ -616,6 +616,7 @@
 
     @Override
     public default Stream<E> stream() {
-        return Streams.stream(this, StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED);
+        return Streams.stream(Streams.sequentialSpliterator(this),
+                              StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED);
     }
 }
--- a/src/share/classes/java/util/Optional.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/src/share/classes/java/util/Optional.java	Fri Nov 16 15:18:25 2012 -0500
@@ -36,7 +36,7 @@
  *
  * @author Brian Goetz
  */
-public class Optional<T> {
+public final class Optional<T> {
     /**
      * Common instance for {@code empty()}.
      */
--- a/src/share/classes/java/util/Set.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/src/share/classes/java/util/Set.java	Fri Nov 16 15:18:25 2012 -0500
@@ -389,6 +389,7 @@
 
     @Override
     default Stream<E> stream() {
-        return Streams.stream(this, StreamOpFlags.IS_SIZED | StreamOpFlags.IS_DISTINCT);
+        return Streams.stream(Streams.sequentialSpliterator(this),
+                              StreamOpFlags.IS_SIZED | StreamOpFlags.IS_DISTINCT);
     }
 }
--- a/src/share/classes/java/util/SortedSet.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/src/share/classes/java/util/SortedSet.java	Fri Nov 16 15:18:25 2012 -0500
@@ -226,7 +226,8 @@
 
     @Override
     default Stream<E> stream() {
-        return Streams.stream(this, StreamOpFlags.IS_SIZED | StreamOpFlags.IS_DISTINCT
-                                    | StreamOpFlags.IS_SORTED | StreamOpFlags.IS_ORDERED);
+        return Streams.stream(Streams.sequentialSpliterator(this),
+                              StreamOpFlags.IS_SIZED | StreamOpFlags.IS_DISTINCT |
+                              StreamOpFlags.IS_SORTED | StreamOpFlags.IS_ORDERED);
     }
 }
--- a/src/share/classes/java/util/Vector.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/src/share/classes/java/util/Vector.java	Fri Nov 16 15:18:25 2012 -0500
@@ -1216,13 +1216,13 @@
 
     @Override
     public Stream<E> stream() {
-        return Streams.stream(() -> Streams.spliterator((E[]) elementData, 0, elementCount),
+        return Streams.stream(() -> Arrays.spliterator((E[]) elementData, 0, elementCount),
                               StreamOpFlags.IS_ORDERED | StreamOpFlags.IS_SIZED);
     }
 
     @Override
     public Stream<E> parallel() {
-        return Streams.parallel(() -> Streams.spliterator((E[]) elementData, 0, elementCount),
+        return Streams.parallel(() -> Arrays.spliterator((E[]) elementData, 0, elementCount),
                                 StreamOpFlags.IS_ORDERED | StreamOpFlags.IS_SIZED);
     }
 }
--- a/src/share/classes/java/util/logging/Logger.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/src/share/classes/java/util/logging/Logger.java	Fri Nov 16 15:18:25 2012 -0500
@@ -588,7 +588,7 @@
     }
 
     /**
-     * Log a message, with one object parameter.
+     * Log a message, with one object parameter.ys
      * <p>
      * If the logger is currently enabled for the given message
      * level then a corresponding LogRecord is created and forwarded
--- a/src/share/classes/java/util/stream/Spliterator.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/src/share/classes/java/util/stream/Spliterator.java	Fri Nov 16 15:18:25 2012 -0500
@@ -35,9 +35,12 @@
  * phase divides the elements of the data structures so that they may be
  * processed concurrently. The split phase is intended to be repeated
  * recursively until the number of elements per split reaches a threshold where
- * further splitting would only generate additional cost.
+ * further splitting would only generate additional cost.  Most methods of
+ * {@code Spliterator} are applicable primarily to one phase or the other.
  * <p/>
- * The traversal phase applies operations upon the elements of a single split.
+ * The traversal phase allows you to obtain access to the elements of the current
+ * split, and begins on first call to the {@code iterator()} or {@code forEach()}
+ * methods.
  * <p/>
  * @author Brian Goetz
  * @author Doug Lea
@@ -47,6 +50,7 @@
     /**
      * Return the number of calls to {@code split()} that will naturally divide
      * the data structure. Each of the splits may suggest additional splitting.
+     * The result of calling this method during the traversal phase is unspecified.
      * <p/>
      * @return The number of splits available for this Spliterator. May be zero
      * indicating that no additional splits are recommended or possible.
@@ -57,7 +61,8 @@
      * Returns a Spliterator covering some portion of the elements, guaranteed
      * not to overlap with those retained by this Spliterator. After invoking
      * this method, the current Spliterator will <em>not</em> cover the elements
-     * of the returned Spliterator.
+     * of the returned Spliterator.  The result of calling this method during the
+     * traversal phase is unspecified.
      * <p/>
      * @return a Spliterator covering the some portion, possibly empty, of the
      * data structure elements.
@@ -69,14 +74,16 @@
 
     /**
      * Return the Iterator covering the remaining elements. The same iterator
-     * instance is returned for every invocation.
+     * instance is returned for every invocation.  This method initiates the
+     * traversal phase.
      * <p/>
      * @return the iterator of the remaining elements.
      */
     Iterator<T> iterator();
 
     /**
-     * Provides any remaining elements into the provided sink.
+     * Provides any remaining elements into the provided sink.  This method initiates
+     * the traversal phase if it has not already been initiated.
      *
      * @param block The sink to which elements will be provided.
      */
@@ -87,11 +94,12 @@
         }
     }
 
-   /**
+    /**
      * Returns the count of elements currently retained by this Stream.
      * This is the count which would be processed by {@code into()} or via
      * {@code iterator()}. If the element count cannot be computed exactly and
-     * cheaply then {@code -1} is returned.
+     * cheaply then {@code -1} is returned.  The result of calling this method
+     * during the traversal phase is unspecified.
      * <p/>
      * @return The number of remaining elements or {@code -1} if the remaining
      * element count is unavailable.
@@ -104,7 +112,8 @@
      * Returns an estimate, possibly inexact, of the count of elements currently
      * retained by this Stream. This is the count which would be processed by
      * {@code into()} or via {@code iterator()}. If an element count estimate
-     * cannot be computed cheaply then {@code -1} is returned.
+     * cannot be computed cheaply then {@code -1} is returned. The result of calling
+     * this method during the traversal phase is unspecified.
      * <p>
      * If {@code getSizeIfKnown()} returns a non-negative integer then
      * {@code estimateSize()} <strong>must</strong> return the same value.
@@ -119,7 +128,8 @@
     /**
      * Return {@code true} if this Spliterator and all returned Spliterators
      * provide non-negative size information from {@code getSizeIfKnown} and
-     * {@code estimateSize}.
+     * {@code estimateSize}.  The result of calling this method during the
+     * traversal phase is unspecified.
      * <p/>
      * @return {@code true} if size information is available for this
      * Spliterator and all sub-splits.
--- a/src/share/classes/java/util/stream/Streams.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/src/share/classes/java/util/stream/Streams.java	Fri Nov 16 15:18:25 2012 -0500
@@ -35,69 +35,37 @@
  * @author Brian Goetz
  */
 public class Streams {
-    private static final Stream EMPTY_STREAM = stream(Collections.emptyList(), 0);
 
     private Streams() {
         throw new Error("no instances");
     }
 
-    // Stream
+    // Spliterator
 
-    public static<U, T extends Sized & Iterable<U>> Stream<U> stream(T entity, int flags) {
-        return stream(new Spliterator.Sequential<U>() {
-            @Override
-            public Iterator<U> iterator() {
-                return entity.iterator();
-            }
-
-            @Override
-            public void forEach(Block<? super U> block) {
-                entity.forEach(block);
-            }
-
-            @Override
-            public int getSizeIfKnown() {
-                return entity.size();
-            }
-        }, StreamOpFlags.IS_SIZED | flags);
+    @SuppressWarnings("unchecked")
+    public static<T> Spliterator<T> emptySpliterator() {
+        return (Spliterator<T>) EMPTY_SPLITERATOR;
     }
 
-    public static<U, T extends Iterable<U>> Stream<U> stream(T entity, Sized sizeProvider, int flags) {
-        return stream(new Spliterator.Sequential<U>() {
-            @Override
-            public Iterator<U> iterator() {
-                return entity.iterator();
-            }
-
-            @Override
-            public void forEach(Block<? super U> block) {
-                entity.forEach(block);
-            }
-
-            @Override
-            public int getSizeIfKnown() {
-                return sizeProvider.size();
-            }
-        }, StreamOpFlags.IS_SIZED | flags);
+    public static<U, T extends Sized & Iterable<U>> Spliterator<U> sequentialSpliterator(T entity) {
+        Objects.requireNonNull(entity);
+        return new IterableSequentialSpliterator<>(entity, entity);
     }
 
-    public static<U, T extends Iterable<U>> Stream<U> stream(T entity, int flags) {
-        return stream(new Spliterator.Sequential<U>() {
-            @Override
-            public Iterator<U> iterator() {
-                return entity.iterator();
-            }
-
-            @Override
-            public void forEach(Block<? super U> block) {
-                entity.forEach(block);
-            }
-            // @@@ Mask off sized if set ?
-        }, flags);
+    public static<U, T extends Iterable<U>> Spliterator<U> sequentialSpliterator(T entity, Sized sizeProvider) {
+        Objects.requireNonNull(entity);
+        Objects.requireNonNull(sizeProvider);
+        return new IterableSequentialSpliterator<>(entity, sizeProvider);
     }
 
-    public static<U, T extends Iterator<U>> Stream<U> stream(T iterator, int flags) {
-        return stream(new Spliterator.Sequential<U>() {
+    public static<U, T extends Iterable<U>> Spliterator<U> sequentialSpliterator(T entity) {
+        Objects.requireNonNull(entity);
+        return new IterableSequentialSpliterator<>(entity);
+    }
+
+    public static<U, T extends Iterator<U>> Spliterator<U> sequentialSpliterator(T iterator) {
+        Objects.requireNonNull(iterator);
+        return new Spliterator.Sequential<U>() {
             @Override
             public Iterator<U> iterator() {
                 return iterator;
@@ -107,60 +75,37 @@
             public void forEach(Block<? super U> block) {
                 iterator.forEach(block);
             }
-            // @@@ Mask off sized if set ?
-        }, flags);
+        };
+    }
+
+    // Stream
+
+    @SuppressWarnings("unchecked")
+    public static<T> Stream<T> emptyStream() {
+        // Can't return a singleton since streams have mutable state
+        return stream(Streams.<T>emptySpliterator(), 0);
     }
 
     public static<U> Stream<U> stream(Spliterator<U> spliterator, int flags) {
+        Objects.requireNonNull(spliterator);
         return new ReferencePipeline<>(spliterator, flags);
     }
 
     public static <T> Stream<T> stream(Supplier<Spliterator<T>> proxy, int flags) {
+        Objects.requireNonNull(proxy);
         return new ReferencePipeline<>(new ProxySpliterator<>(proxy), flags);
     }
 
-    public static <T> Spliterator<T> spliterator(T[] source) {
-        return spliterator(source, 0, source.length);
-    }
-
-    public static <T> Spliterator<T> spliterator(T[] source, int offset, int length) {
-        return new ArraySpliterator<>(source, offset, length);
-    }
-
-    public static <T> Stream<T> stream(T[] source) {
-        return stream(source, 0, source.length);
-    }
-
-    public static <T> Stream<T> stream(T[] source, int offset, int length) {
-        // Note use of full-service Spliterator here -- harmless because PARALLEL flag is not set
-        return stream((Spliterator) new ArraySpliterator<>(source, offset, length),
-                      StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED);
-    }
-
-    public static <T> Stream<T> parallel(T[] source) {
-        return Streams.parallel(source, 0, source.length);
-    }
-
-    public static <T> Stream<T> parallel(T[] source, int offset, int length) {
-        return parallel(new ArraySpliterator<>(source, offset, length),
-                        StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED);
-    }
-
     public static<T> Stream<T> parallel(Spliterator<T> spliterator, int flags) {
-        if (spliterator.getSizeIfKnown() >= 0)
-            flags |= StreamOpFlags.IS_SIZED;
+        Objects.requireNonNull(spliterator);
         return new ReferencePipeline<>(spliterator, flags | StreamOpFlags.IS_PARALLEL);
     }
 
     public static <T> Stream<T> parallel(Supplier<Spliterator<T>> proxy, int flags) {
+        Objects.requireNonNull(proxy);
         return new ReferencePipeline<>(new ProxySpliterator<>(proxy), flags | StreamOpFlags.IS_PARALLEL);
     }
 
-    @SuppressWarnings("unchecked")
-    public static<T> Stream<T> emptyStream() {
-        return EMPTY_STREAM;
-    }
-
     // Infinite streams
 
     public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
@@ -173,7 +118,7 @@
                 return t = (t == null) ? seed : f.operate(t);
             }
         };
-        return stream(iterator, StreamOpFlags.IS_ORDERED);
+        return stream(sequentialSpliterator(iterator), StreamOpFlags.IS_ORDERED);
     }
 
     public static<T> Stream<T> repeat(T t) {
@@ -183,7 +128,7 @@
     public static<T> Stream<T> repeat(final int n, final T t) {
         if (n < 0) {
             InfiniteIterator<T> iterator = () -> t;
-            return stream(iterator, StreamOpFlags.IS_ORDERED);
+            return stream(sequentialSpliterator(iterator), StreamOpFlags.IS_ORDERED);
         }
         else
             return repeatedly(n, () -> t);
@@ -198,7 +143,7 @@
 
         if (n < 0) {
             InfiniteIterator<T> iterator = () -> f.get();
-            return stream(iterator, StreamOpFlags.IS_ORDERED);
+            return stream(sequentialSpliterator(iterator), StreamOpFlags.IS_ORDERED);
         }
         else {
             final Iterator<T> repeatedly = new Iterator<T>() {
@@ -220,7 +165,7 @@
                 }
             };
 
-            return stream(repeatedly, StreamOpFlags.IS_ORDERED);
+            return stream(sequentialSpliterator(repeatedly), StreamOpFlags.IS_ORDERED);
         }
     }
 
@@ -245,9 +190,11 @@
             }
         };
 
-        return stream(cycle, StreamOpFlags.IS_ORDERED);
+        return stream(sequentialSpliterator(cycle), StreamOpFlags.IS_ORDERED);
     }
 
+    // Spliterator implementations
+
     private static final Spliterator<?> EMPTY_SPLITERATOR = new Spliterator<Object>() {
             @Override
             public int getNaturalSplits() {
@@ -279,10 +226,6 @@
             }
         };
 
-    public static<T> Spliterator<T> emptySpliterator() {
-        return (Spliterator<T>) EMPTY_SPLITERATOR;
-    }
-
     public interface InfiniteIterator<T> extends Iterator<T> {
         @Override
         public default boolean hasNext() {
@@ -290,92 +233,45 @@
         }
     }
 
-    private static class ArrayIterator<T> implements Iterator<T> {
-        protected final T[] elements;
-        protected final int endOffset;
-        protected int curOffset;
+    private static class IterableSequentialSpliterator<T> implements Spliterator.Sequential<T> {
+        private final Iterable<T> iterable;
+        private final Sized sizeProvider;
 
-        private ArrayIterator(T[] elements, int startOffset, int len) {
-            this.elements = Objects.requireNonNull(elements);
-            this.endOffset = startOffset + len;
-            this.curOffset = startOffset;
+        private Iterator<T> iterator;
+        private int size;
 
-            assert curOffset >= 0 : "offset not positive";
-            assert endOffset >= curOffset : "end lower than start";
-            assert (curOffset < elements.length) || (0 == len && 0 == curOffset) : "offset not in array";
-            assert endOffset <= elements.length : "end not in array";
+        private IterableSequentialSpliterator(Iterable<T> iterable) {
+            this(iterable, null);
         }
 
-        public T next() {
-            if (!hasNext()) {
-                throw new NoSuchElementException();
+        private IterableSequentialSpliterator(Iterable<T> iterable, Sized sizeProvider) {
+            this.iterable = iterable;
+            this.sizeProvider = sizeProvider;
+        }
+
+        private void initiate() {
+            if (iterator == null) {
+                iterator = iterable.iterator();
+                size = (sizeProvider != null) ? sizeProvider.size() : -1;
             }
-
-            return elements[curOffset++];
         }
 
         @Override
-        public final boolean hasNext() {
-            return curOffset < endOffset;
-        }
-    }
-
-    private static class ArraySpliterator<T> extends ArrayIterator<T> implements Spliterator<T> {
-        boolean traversing = false;
-
-        ArraySpliterator(T[] elements) {
-            this(elements, 0, elements.length);
+        public Iterator<T> iterator() {
+            initiate();
+            return iterator;
         }
 
-        ArraySpliterator(T[] elements, int offset, int length) {
-            super(elements, offset, length);
+        @Override
+        public void forEach(Block<? super T> block) {
+            initiate();
+            iterator.forEach(block);
         }
 
         @Override
         public int getSizeIfKnown() {
-            return endOffset - curOffset;
-        }
-
-        @Override
-        public int estimateSize() {
-            return getSizeIfKnown();
-        }
-
-        @Override
-        public boolean isPredictableSplits() {
-            return true;
-        }
-
-        @Override
-        public void forEach(Block<? super T> block) {
-            traversing = true;
-            for (int i= curOffset; i<endOffset; i++) {
-                block.apply(elements[i]);
-            }
-            // update only once; reduce heap write traffic
-            curOffset = endOffset;
-        }
-
-        @Override
-        public int getNaturalSplits() {
-            return (endOffset - curOffset > 1) ? 1 : 0;
-        }
-
-        @Override
-        public Spliterator<T> split() {
-            if (traversing) {
-                throw new IllegalStateException("split after starting traversal");
-            }
-            int t = (endOffset - curOffset) / 2;
-            Spliterator<T> ret = new ArraySpliterator<>(elements, curOffset, t);
-            curOffset += t;
-            return ret;
-        }
-
-        @Override
-        public Iterator<T> iterator() {
-            traversing = true;
-            return this;
+            initiate();
+            return size;
         }
     }
 
--- a/src/share/classes/java/util/stream/op/Nodes.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/src/share/classes/java/util/stream/op/Nodes.java	Fri Nov 16 15:18:25 2012 -0500
@@ -718,12 +718,14 @@
 
         @Override
         public Stream<T> stream() {
-            return Streams.stream(this, StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED);
+            return Streams.stream(Streams.sequentialSpliterator(this),
+                                  StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED);
         }
 
         @Override
         public Stream<T> parallel() {
-            return Streams.parallel(spliterator(), StreamOpFlags.IS_ORDERED);
+            return Streams.parallel(spliterator(),
+                                    StreamOpFlags.IS_ORDERED);
         }
 
         // </editor-fold>
--- a/test-ng/tests/org/openjdk/tests/java/util/FillableStringTest.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/FillableStringTest.java	Fri Nov 16 15:18:25 2012 -0500
@@ -36,7 +36,7 @@
 @Test(groups = "lib")
 public class FillableStringTest {
     public Stream<String> generate() {
-        return Arrays.asStream("one", "two", "three", "four", "five", "six")
+        return Arrays.asList("one", "two", "three", "four", "five", "six").stream()
                 .filter(s->s.length() > 3)
                 .map(String::toUpperCase);
     }
--- a/test-ng/tests/org/openjdk/tests/java/util/stream/op/MapOpTest.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/stream/op/MapOpTest.java	Fri Nov 16 15:18:25 2012 -0500
@@ -62,6 +62,9 @@
 
         exerciseOps(countTo(0), s -> s.map(e -> e), countTo(0));
         exerciseOps(countTo(1000), s -> s.map(e -> e), countTo(1000));
+        // @@@ Force cast to integer so output is Stream<Integer> rather an IntStream
+        //     this just ensures that no warnings are logged about boxing
+        //     when the result is compared with the output
         exerciseOps(countTo(1000), s -> s.map(e -> (Integer) (1000+e)), range(1001, 2000));
     }
 
--- a/test-ng/tests/org/openjdk/tests/java/util/stream/op/ToArrayOpTest.java	Fri Nov 16 17:03:41 2012 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/stream/op/ToArrayOpTest.java	Fri Nov 16 15:18:25 2012 -0500
@@ -33,7 +33,6 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.function.Block;
-import java.util.stream.Stream;
 import java.util.stream.StreamOpFlags;
 import java.util.stream.Streams;
 import java.util.stream.op.*;
@@ -111,19 +110,19 @@
 
         {
             Node<Integer> node = Nodes.node(l);
-            Object[] output = Streams.stream(node, StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED).toArray();
+            Object[] output = Streams.stream(Streams.sequentialSpliterator(node), StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED).toArray();
             assertEquals(Arrays.asList(output), l);
         }
 
         {
             Node<Integer> node = Nodes.node(l.toArray(new Integer[l.size()]));
-            Object[] output = Streams.stream(node, StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED).toArray();
+            Object[] output = Streams.stream(Streams.sequentialSpliterator(node), StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED).toArray();
             assertEquals(Arrays.asList(output), l);
         }
 
         {
             Node<Integer> node = tree(l);
-            Object[] output = Streams.stream(node, StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED).toArray();
+            Object[] output = Streams.stream(Streams.sequentialSpliterator(node), StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED).toArray();
             assertEquals(Arrays.asList(output), l);
         }
 
@@ -132,7 +131,7 @@
             for (Integer i : l) {
                 nodeBuilder.apply(i);
             }
-            Object[] output = Streams.stream(nodeBuilder, StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED).toArray();
+            Object[] output = Streams.stream(Streams.sequentialSpliterator(nodeBuilder), StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED).toArray();
             assertEquals(Arrays.asList(output), l);
         }
 
@@ -143,13 +142,13 @@
                 nodeBuilder.apply(i);
             }
             nodeBuilder.end();
-            Object[] output = Streams.stream(nodeBuilder, StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED).toArray();
+            Object[] output = Streams.stream(Streams.sequentialSpliterator(nodeBuilder), StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED).toArray();
             assertEquals(Arrays.asList(output), l);
         }
 
         {
             Node<Integer> node = Nodes.node(l);
-            Object[] output = Streams.stream(node, StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED).sequential().toArray();
+            Object[] output = Streams.stream(Streams.sequentialSpliterator(node), StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED).sequential().toArray();
             assertEquals(Arrays.asList(output), l);
         }