Hide non-standard APIs from java.util.stream

Change-Id: Idcd5c9390ad0305d075a6b1ea070e1f9aa304797
Review-Link: https://gwt-review.googlesource.com/#/c/17540/
diff --git a/user/super/com/google/gwt/emul/java/util/stream/Collector.java b/user/super/com/google/gwt/emul/java/util/stream/Collector.java
index ce41e92..c3907ef 100644
--- a/user/super/com/google/gwt/emul/java/util/stream/Collector.java
+++ b/user/super/com/google/gwt/emul/java/util/stream/Collector.java
@@ -13,7 +13,6 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package java.util.stream;
 
 import static javaemul.internal.InternalPreconditions.checkNotNull;
@@ -90,73 +89,4 @@
   BinaryOperator<A> combiner();
 
   Function<A,R> finisher();
-
-  /**
-   * Simple internal implementation of a collector, holding each of the functions in a field.
-   */
-  final class CollectorImpl<T, A, R> implements Collector<T, A, R> {
-    private final Supplier<A> supplier;
-    private final BiConsumer<A, T> accumulator;
-    private final Set<Characteristics> characteristics;
-    private final BinaryOperator<A> combiner;
-    private final Function<A, R> finisher;
-
-    public CollectorImpl(
-        Supplier<A> supplier,
-        BiConsumer<A, T> accumulator,
-        BinaryOperator<A> combiner,
-        Function<A, R> finisher,
-        Characteristics... characteristics) {
-      this.supplier = supplier;
-      this.accumulator = accumulator;
-      if (characteristics.length == 0) {
-        this.characteristics = Collections.emptySet();
-      } else if (characteristics.length == 1) {
-        this.characteristics = Collections.singleton(characteristics[0]);
-      } else {
-        this.characteristics =
-            Collections.unmodifiableSet(EnumSet.of(characteristics[0], characteristics));
-      }
-      this.combiner = combiner;
-      this.finisher = finisher;
-    }
-
-    public CollectorImpl(
-        Supplier<A> supplier,
-        BiConsumer<A, T> accumulator,
-        BinaryOperator<A> combiner,
-        Function<A, R> finisher,
-        Set<Characteristics> characteristics) {
-      this.supplier = supplier;
-      this.accumulator = accumulator;
-      this.combiner = combiner;
-      this.finisher = finisher;
-      this.characteristics = characteristics;
-    }
-
-    @Override
-    public Supplier<A> supplier() {
-      return supplier;
-    }
-
-    @Override
-    public BiConsumer<A, T> accumulator() {
-      return accumulator;
-    }
-
-    @Override
-    public BinaryOperator<A> combiner() {
-      return combiner;
-    }
-
-    @Override
-    public Function<A, R> finisher() {
-      return finisher;
-    }
-
-    @Override
-    public Set<Characteristics> characteristics() {
-      return characteristics;
-    }
-  }
 }
\ No newline at end of file
diff --git a/user/super/com/google/gwt/emul/java/util/stream/CollectorImpl.java b/user/super/com/google/gwt/emul/java/util/stream/CollectorImpl.java
new file mode 100644
index 0000000..9202a28
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/util/stream/CollectorImpl.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package java.util.stream;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * Simple internal implementation of a collector, holding each of the functions in a field.
+ */
+final class CollectorImpl<T, A, R> implements Collector<T, A, R> {
+  private final Supplier<A> supplier;
+  private final BiConsumer<A, T> accumulator;
+  private final Set<Characteristics> characteristics;
+  private final BinaryOperator<A> combiner;
+  private final Function<A, R> finisher;
+
+  public CollectorImpl(
+      Supplier<A> supplier,
+      BiConsumer<A, T> accumulator,
+      BinaryOperator<A> combiner,
+      Function<A, R> finisher,
+      Characteristics... characteristics) {
+    this.supplier = supplier;
+    this.accumulator = accumulator;
+    if (characteristics.length == 0) {
+      this.characteristics = Collections.emptySet();
+    } else if (characteristics.length == 1) {
+      this.characteristics = Collections.singleton(characteristics[0]);
+    } else {
+      this.characteristics =
+          Collections.unmodifiableSet(EnumSet.of(characteristics[0], characteristics));
+    }
+    this.combiner = combiner;
+    this.finisher = finisher;
+  }
+
+  public CollectorImpl(
+      Supplier<A> supplier,
+      BiConsumer<A, T> accumulator,
+      BinaryOperator<A> combiner,
+      Function<A, R> finisher,
+      Set<Characteristics> characteristics) {
+    this.supplier = supplier;
+    this.accumulator = accumulator;
+    this.combiner = combiner;
+    this.finisher = finisher;
+    this.characteristics = characteristics;
+  }
+
+  @Override
+  public Supplier<A> supplier() {
+    return supplier;
+  }
+
+  @Override
+  public BiConsumer<A, T> accumulator() {
+    return accumulator;
+  }
+
+  @Override
+  public BinaryOperator<A> combiner() {
+    return combiner;
+  }
+
+  @Override
+  public Function<A, R> finisher() {
+    return finisher;
+  }
+
+  @Override
+  public Set<Characteristics> characteristics() {
+    return characteristics;
+  }
+}
diff --git a/user/super/com/google/gwt/emul/java/util/stream/Collectors.java b/user/super/com/google/gwt/emul/java/util/stream/Collectors.java
index c19dfef..65edfa5 100644
--- a/user/super/com/google/gwt/emul/java/util/stream/Collectors.java
+++ b/user/super/com/google/gwt/emul/java/util/stream/Collectors.java
@@ -64,7 +64,7 @@
 
   public static <T, A, R, RR> Collector<T, A, RR> collectingAndThen(
       Collector<T, A, R> downstream, Function<R, RR> finisher) {
-    return new Collector.CollectorImpl<>(
+    return new CollectorImpl<>(
         downstream.supplier(),
         downstream.accumulator(),
         downstream.combiner(),
@@ -163,15 +163,15 @@
 
   public static <T, U, A, R> Collector<T, ?, R> mapping(
       final Function<? super T, ? extends U> mapper, final Collector<? super U, A, R> downstream) {
-    return new Collector.CollectorImpl<>(
+    return new CollectorImpl<>(
         downstream.supplier(),
-        (BiConsumer<A, T>) (A a, T t) -> {
-          downstream.accumulator().accept(a, mapper.apply(t));
-        },
+        (BiConsumer<A, T>)
+            (A a, T t) -> {
+              downstream.accumulator().accept(a, mapper.apply(t));
+            },
         downstream.combiner(),
         downstream.finisher(),
-        downstream.characteristics()
-    );
+        downstream.characteristics());
   }
 
   public static <T> Collector<T,?,Optional<T>> maxBy(Comparator<? super T> comparator) {
diff --git a/user/super/com/google/gwt/emul/java/util/stream/DoubleStream.java b/user/super/com/google/gwt/emul/java/util/stream/DoubleStream.java
index bfe782c..75b0474 100644
--- a/user/super/com/google/gwt/emul/java/util/stream/DoubleStream.java
+++ b/user/super/com/google/gwt/emul/java/util/stream/DoubleStream.java
@@ -13,22 +13,17 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package java.util.stream;
 
-import static javaemul.internal.InternalPreconditions.checkNotNull;
 import static javaemul.internal.InternalPreconditions.checkState;
 
 import java.util.Arrays;
-import java.util.Comparator;
 import java.util.DoubleSummaryStatistics;
-import java.util.HashSet;
 import java.util.OptionalDouble;
 import java.util.PrimitiveIterator;
 import java.util.Spliterator;
 import java.util.Spliterators;
 import java.util.function.BiConsumer;
-import java.util.function.Consumer;
 import java.util.function.DoubleBinaryOperator;
 import java.util.function.DoubleConsumer;
 import java.util.function.DoubleFunction;
@@ -37,8 +32,6 @@
 import java.util.function.DoubleToIntFunction;
 import java.util.function.DoubleToLongFunction;
 import java.util.function.DoubleUnaryOperator;
-import java.util.function.IntConsumer;
-import java.util.function.LongConsumer;
 import java.util.function.ObjDoubleConsumer;
 import java.util.function.Supplier;
 
@@ -49,18 +42,23 @@
 public interface DoubleStream extends BaseStream<Double, DoubleStream> {
 
   /**
-   * Value holder for various stream operations.
+   * See <a
+   * href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/DoubleStream.Builder.html">the
+   * official Java API doc</a> for details.
    */
-  static final class ValueConsumer implements DoubleConsumer {
-    double value;
-
+  interface Builder extends DoubleConsumer {
     @Override
-    public void accept(double value) {
-      this.value = value;
+    void accept(double t);
+
+    default DoubleStream.Builder add(double t) {
+      accept(t);
+      return this;
     }
+
+    DoubleStream build();
   }
 
-  static DoubleStream.Builder builder() {
+  static Builder builder() {
     return new Builder() {
       private double[] items = new double[0];
 
@@ -124,7 +122,7 @@
           }
         };
 
-    DoubleStream result = new DoubleStreamSource(null, spliterator);
+    DoubleStream result = new DoubleStreamImpl(null, spliterator);
 
     result.onClose(a::close);
     result.onClose(b::close);
@@ -133,7 +131,7 @@
   }
 
   static DoubleStream empty() {
-    return new EmptyDoubleStreamSource(null);
+    return new DoubleStreamImpl.Empty(null);
   }
 
   static DoubleStream generate(DoubleSupplier s) {
@@ -176,23 +174,6 @@
     return of(new double[] {t});
   }
 
-  /**
-   * See
-   * <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/DoubleStream.Builder.html">
-   * the official Java API doc</a> for details.
-   */
-  public interface Builder extends DoubleConsumer {
-    @Override
-    void accept(double t);
-
-    default DoubleStream.Builder add(double t) {
-      accept(t);
-      return this;
-    }
-
-    DoubleStream build();
-  }
-
   boolean allMatch(DoublePredicate predicate);
 
   boolean anyMatch(DoublePredicate predicate);
@@ -262,763 +243,4 @@
   DoubleSummaryStatistics summaryStatistics();
 
   double[] toArray();
-
-  /**
-   * Represents an empty stream, doing nothing for all methods.
-   */
-  static class EmptyDoubleStreamSource extends TerminatableStream<EmptyDoubleStreamSource>
-      implements DoubleStream {
-    public EmptyDoubleStreamSource(TerminatableStream<?> previous) {
-      super(previous);
-    }
-
-    @Override
-    public DoubleStream filter(DoublePredicate predicate) {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public DoubleStream map(DoubleUnaryOperator mapper) {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public <U> Stream<U> mapToObj(DoubleFunction<? extends U> mapper) {
-      throwIfTerminated();
-      return new Stream.EmptyStreamSource<U>(this);
-    }
-
-    @Override
-    public IntStream mapToInt(DoubleToIntFunction mapper) {
-      throwIfTerminated();
-      return new IntStream.EmptyIntStreamSource(this);
-    }
-
-    @Override
-    public LongStream mapToLong(DoubleToLongFunction mapper) {
-      throwIfTerminated();
-      return new LongStream.EmptyLongStreamSource(this);
-    }
-
-    @Override
-    public DoubleStream flatMap(DoubleFunction<? extends DoubleStream> mapper) {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public DoubleStream distinct() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public DoubleStream sorted() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public DoubleStream peek(DoubleConsumer action) {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public DoubleStream limit(long maxSize) {
-      throwIfTerminated();
-      checkState(maxSize >= 0, "maxSize may not be negative");
-      return this;
-    }
-
-    @Override
-    public DoubleStream skip(long n) {
-      throwIfTerminated();
-      checkState(n >= 0, "n may not be negative");
-      return this;
-    }
-
-    @Override
-    public void forEach(DoubleConsumer action) {
-      terminate();
-      // do nothing
-    }
-
-    @Override
-    public void forEachOrdered(DoubleConsumer action) {
-      terminate();
-      // do nothing
-    }
-
-    @Override
-    public double[] toArray() {
-      terminate();
-      return new double[0];
-    }
-
-    @Override
-    public double reduce(double identity, DoubleBinaryOperator op) {
-      terminate();
-      return identity;
-    }
-
-    @Override
-    public OptionalDouble reduce(DoubleBinaryOperator op) {
-      terminate();
-      return OptionalDouble.empty();
-    }
-
-    @Override
-    public <R> R collect(
-        Supplier<R> supplier, ObjDoubleConsumer<R> accumulator, BiConsumer<R, R> combiner) {
-      terminate();
-      return supplier.get();
-    }
-
-    @Override
-    public double sum() {
-      terminate();
-      return 0;
-    }
-
-    @Override
-    public OptionalDouble min() {
-      terminate();
-      return OptionalDouble.empty();
-    }
-
-    @Override
-    public OptionalDouble max() {
-      terminate();
-      return OptionalDouble.empty();
-    }
-
-    @Override
-    public long count() {
-      terminate();
-      return 0;
-    }
-
-    @Override
-    public OptionalDouble average() {
-      terminate();
-      return OptionalDouble.empty();
-    }
-
-    @Override
-    public DoubleSummaryStatistics summaryStatistics() {
-      terminate();
-      return new DoubleSummaryStatistics();
-    }
-
-    @Override
-    public boolean anyMatch(DoublePredicate predicate) {
-      terminate();
-      return false;
-    }
-
-    @Override
-    public boolean allMatch(DoublePredicate predicate) {
-      terminate();
-      return true;
-    }
-
-    @Override
-    public boolean noneMatch(DoublePredicate predicate) {
-      terminate();
-      return true;
-    }
-
-    @Override
-    public OptionalDouble findFirst() {
-      terminate();
-      return OptionalDouble.empty();
-    }
-
-    @Override
-    public OptionalDouble findAny() {
-      terminate();
-      return OptionalDouble.empty();
-    }
-
-    @Override
-    public Stream<Double> boxed() {
-      throwIfTerminated();
-      return new Stream.EmptyStreamSource<Double>(this);
-    }
-
-    @Override
-    public DoubleStream sequential() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public DoubleStream parallel() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public PrimitiveIterator.OfDouble iterator() {
-      return Spliterators.iterator(spliterator());
-    }
-
-    @Override
-    public Spliterator.OfDouble spliterator() {
-      terminate();
-      return Spliterators.emptyDoubleSpliterator();
-    }
-
-    @Override
-    public boolean isParallel() {
-      throwIfTerminated();
-      return false;
-    }
-
-    @Override
-    public DoubleStream unordered() {
-      throwIfTerminated();
-      return this;
-    }
-  }
-
-  /**
-   * Double to Int map spliterator.
-   */
-  static final class MapToIntSpliterator extends Spliterators.AbstractIntSpliterator {
-    private final DoubleToIntFunction map;
-    private final Spliterator.OfDouble original;
-
-    public MapToIntSpliterator(DoubleToIntFunction map, Spliterator.OfDouble original) {
-      super(
-          original.estimateSize(),
-          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
-      checkNotNull(map);
-      this.map = map;
-      this.original = original;
-    }
-
-    @Override
-    public boolean tryAdvance(final IntConsumer action) {
-      return original.tryAdvance((double u) -> action.accept(map.applyAsInt(u)));
-    }
-  }
-
-  /**
-   * Double to Object map spliterator.
-   * @param <T> the type of Object in the spliterator
-   */
-  static final class MapToObjSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
-    private final DoubleFunction<? extends T> map;
-    private final Spliterator.OfDouble original;
-
-    public MapToObjSpliterator(DoubleFunction<? extends T> map, Spliterator.OfDouble original) {
-      super(
-          original.estimateSize(),
-          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
-      checkNotNull(map);
-      this.map = map;
-      this.original = original;
-    }
-
-    @Override
-    public boolean tryAdvance(final Consumer<? super T> action) {
-      return original.tryAdvance((double u) -> action.accept(map.apply(u)));
-    }
-  }
-
-  /**
-   * Double to Long map spliterator.
-   */
-  static final class MapToLongSpliterator extends Spliterators.AbstractLongSpliterator {
-    private final DoubleToLongFunction map;
-    private final Spliterator.OfDouble original;
-
-    public MapToLongSpliterator(DoubleToLongFunction map, Spliterator.OfDouble original) {
-      super(
-          original.estimateSize(),
-          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
-      checkNotNull(map);
-      this.map = map;
-      this.original = original;
-    }
-
-    @Override
-    public boolean tryAdvance(final LongConsumer action) {
-      return original.tryAdvance((double u) -> action.accept(map.applyAsLong(u)));
-    }
-  }
-
-  /**
-   * Double to Double map spliterator.
-   */
-  static final class MapToDoubleSpliterator extends Spliterators.AbstractDoubleSpliterator {
-    private final DoubleUnaryOperator map;
-    private final Spliterator.OfDouble original;
-
-    public MapToDoubleSpliterator(DoubleUnaryOperator map, Spliterator.OfDouble original) {
-      super(
-          original.estimateSize(),
-          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
-      checkNotNull(map);
-      this.map = map;
-      this.original = original;
-    }
-
-    @Override
-    public boolean tryAdvance(final DoubleConsumer action) {
-      return original.tryAdvance((double u) -> action.accept(map.applyAsDouble(u)));
-    }
-  }
-
-  /**
-   * Double filter spliterator.
-   */
-  static final class FilterSpliterator extends Spliterators.AbstractDoubleSpliterator {
-    private final DoublePredicate filter;
-    private final Spliterator.OfDouble original;
-
-    private boolean found;
-
-    public FilterSpliterator(DoublePredicate filter, Spliterator.OfDouble original) {
-      super(original.estimateSize(), original.characteristics() & ~Spliterator.SIZED);
-      checkNotNull(filter);
-      this.filter = filter;
-      this.original = original;
-    }
-
-    @Override
-    public Comparator<? super Double> getComparator() {
-      return original.getComparator();
-    }
-
-    @Override
-    public boolean tryAdvance(final DoubleConsumer action) {
-      found = false;
-      while (!found
-          && original.tryAdvance(
-              (double item) -> {
-                if (filter.test(item)) {
-                  found = true;
-                  action.accept(item);
-                }
-              })) {
-        // do nothing, work is done in tryAdvance
-      }
-
-      return found;
-    }
-  }
-
-  /**
-   * Double skip spliterator.
-   */
-  static final class SkipSpliterator extends Spliterators.AbstractDoubleSpliterator {
-    private long skip;
-    private final Spliterator.OfDouble original;
-
-    public SkipSpliterator(long skip, Spliterator.OfDouble original) {
-      super(
-          original.hasCharacteristics(Spliterator.SIZED)
-              ? Math.max(0, original.estimateSize() - skip)
-              : Long.MAX_VALUE,
-          original.characteristics());
-      this.skip = skip;
-      this.original = original;
-    }
-
-    @Override
-    public Comparator<? super Double> getComparator() {
-      return original.getComparator();
-    }
-
-    @Override
-    public boolean tryAdvance(DoubleConsumer action) {
-      while (skip > 0) {
-        if (!original.tryAdvance((double ignore) -> { })) {
-          return false;
-        }
-        skip--;
-      }
-      return original.tryAdvance(action);
-    }
-  }
-
-  /**
-   * Double limit spliterator.
-   */
-  static final class LimitSpliterator extends Spliterators.AbstractDoubleSpliterator {
-    private final long limit;
-    private final Spliterator.OfDouble original;
-    private int position = 0;
-
-    public LimitSpliterator(long limit, Spliterator.OfDouble original) {
-      super(
-          original.hasCharacteristics(Spliterator.SIZED)
-              ? Math.min(original.estimateSize(), limit)
-              : Long.MAX_VALUE,
-          original.characteristics());
-      this.limit = limit;
-      this.original = original;
-    }
-
-    @Override
-    public Comparator<? super Double> getComparator() {
-      return original.getComparator();
-    }
-
-    @Override
-    public boolean tryAdvance(DoubleConsumer action) {
-      if (position >= limit) {
-        return false;
-      }
-
-      boolean result = original.tryAdvance(action);
-      position++;
-      return result;
-    }
-  }
-
-  /**
-   * Main implementation of DoubleStream, wrapping a single spliterator, and an optional parent
-   * stream.
-   */
-  static class DoubleStreamSource extends TerminatableStream<DoubleStreamSource>
-      implements DoubleStream {
-    private final Spliterator.OfDouble spliterator;
-
-    public DoubleStreamSource(TerminatableStream<?> previous, Spliterator.OfDouble spliterator) {
-      super(previous);
-      this.spliterator = spliterator;
-    }
-
-    // terminals
-
-    @Override
-    public void forEach(DoubleConsumer action) {
-      forEachOrdered(action);
-    }
-
-    @Override
-    public void forEachOrdered(DoubleConsumer action) {
-      terminate();
-      spliterator.forEachRemaining(action);
-    }
-
-    @Override
-    public double[] toArray() {
-      terminate();
-      double[] entries = new double[0];
-      // this is legal in js, since the array will be backed by a JS array
-      spliterator.forEachRemaining((double value) -> entries[entries.length] = value);
-
-      return entries;
-    }
-
-    @Override
-    public double reduce(double identity, DoubleBinaryOperator op) {
-      terminate();
-      ValueConsumer holder = new ValueConsumer();
-      holder.value = identity;
-      spliterator.forEachRemaining(
-          (double value) -> {
-            holder.accept(op.applyAsDouble(holder.value, value));
-          });
-      return holder.value;
-    }
-
-    @Override
-    public OptionalDouble reduce(DoubleBinaryOperator op) {
-      ValueConsumer holder = new ValueConsumer();
-      if (spliterator.tryAdvance(holder)) {
-        return OptionalDouble.of(reduce(holder.value, op));
-      }
-      terminate();
-      return OptionalDouble.empty();
-    }
-
-    @Override
-    public <R> R collect(
-        Supplier<R> supplier, ObjDoubleConsumer<R> accumulator, BiConsumer<R, R> combiner) {
-      terminate();
-      final R acc = supplier.get();
-      spliterator.forEachRemaining((double value) -> accumulator.accept(acc, value));
-      return acc;
-    }
-
-    @Override
-    public double sum() {
-      return summaryStatistics().getSum();
-    }
-
-    @Override
-    public OptionalDouble min() {
-      DoubleSummaryStatistics stats = summaryStatistics();
-      if (stats.getCount() == 0) {
-        return OptionalDouble.empty();
-      }
-      return OptionalDouble.of(stats.getMin());
-    }
-
-    @Override
-    public OptionalDouble max() {
-      DoubleSummaryStatistics stats = summaryStatistics();
-      if (stats.getCount() == 0) {
-        return OptionalDouble.empty();
-      }
-      return OptionalDouble.of(stats.getMax());
-    }
-
-    @Override
-    public long count() {
-      terminate();
-      long count = 0;
-      while (spliterator.tryAdvance((double value) -> { })) {
-        count++;
-      }
-      return count;
-    }
-
-    @Override
-    public OptionalDouble average() {
-      DoubleSummaryStatistics stats = summaryStatistics();
-      if (stats.getCount() == 0) {
-        return OptionalDouble.empty();
-      }
-      return OptionalDouble.of(stats.getAverage());
-    }
-
-    @Override
-    public DoubleSummaryStatistics summaryStatistics() {
-      return collect(
-          DoubleSummaryStatistics::new,
-          // TODO switch to a lambda reference once #9340 is fixed
-          (doubleSummaryStatistics, value) -> doubleSummaryStatistics.accept(value),
-          DoubleSummaryStatistics::combine);
-    }
-
-    @Override
-    public boolean anyMatch(DoublePredicate predicate) {
-      return filter(predicate).findFirst().isPresent();
-    }
-
-    @Override
-    public boolean allMatch(DoublePredicate predicate) {
-      return !anyMatch(predicate.negate());
-    }
-
-    @Override
-    public boolean noneMatch(DoublePredicate predicate) {
-      return !anyMatch(predicate);
-    }
-
-    @Override
-    public OptionalDouble findFirst() {
-      terminate();
-      ValueConsumer holder = new ValueConsumer();
-      if (spliterator.tryAdvance(holder)) {
-        return OptionalDouble.of(holder.value);
-      }
-      return OptionalDouble.empty();
-    }
-
-    @Override
-    public OptionalDouble findAny() {
-      return findFirst();
-    }
-
-    @Override
-    public PrimitiveIterator.OfDouble iterator() {
-      return Spliterators.iterator(spliterator());
-    }
-
-    @Override
-    public Spliterator.OfDouble spliterator() {
-      terminate();
-      return spliterator;
-    }
-
-    // end terminals
-
-    // intermediates
-
-    @Override
-    public DoubleStream filter(DoublePredicate predicate) {
-      throwIfTerminated();
-      return new DoubleStreamSource(this, new FilterSpliterator(predicate, spliterator));
-    }
-
-    @Override
-    public DoubleStream map(DoubleUnaryOperator mapper) {
-      throwIfTerminated();
-      return new DoubleStreamSource(this, new MapToDoubleSpliterator(mapper, spliterator));
-    }
-
-    @Override
-    public <U> Stream<U> mapToObj(DoubleFunction<? extends U> mapper) {
-      throwIfTerminated();
-      return new Stream.StreamSource<U>(this, new MapToObjSpliterator<U>(mapper, spliterator));
-    }
-
-    @Override
-    public IntStream mapToInt(DoubleToIntFunction mapper) {
-      throwIfTerminated();
-      return new IntStream.IntStreamSource(this, new MapToIntSpliterator(mapper, spliterator));
-    }
-
-    @Override
-    public LongStream mapToLong(DoubleToLongFunction mapper) {
-      throwIfTerminated();
-      return new LongStream.LongStreamSource(this, new MapToLongSpliterator(mapper, spliterator));
-    }
-
-    @Override
-    public DoubleStream flatMap(DoubleFunction<? extends DoubleStream> mapper) {
-      throwIfTerminated();
-      final Spliterator<? extends DoubleStream> spliteratorOfStreams =
-          new MapToObjSpliterator<DoubleStream>(mapper, spliterator);
-
-      Spliterator.OfDouble flatMapSpliterator =
-          new Spliterators.AbstractDoubleSpliterator(Long.MAX_VALUE, 0) {
-            DoubleStream nextStream;
-            Spliterator.OfDouble next;
-
-            @Override
-            public boolean tryAdvance(DoubleConsumer action) {
-              // look for a new spliterator
-              while (advanceToNextSpliterator()) {
-                // if we have one, try to read and use it
-                if (next.tryAdvance(action)) {
-                  return true;
-                } else {
-                  nextStream.close();
-                  nextStream = null;
-                  // failed, null it out so we can find another
-                  next = null;
-                }
-              }
-              return false;
-            }
-
-            private boolean advanceToNextSpliterator() {
-              while (next == null) {
-                if (!spliteratorOfStreams.tryAdvance(
-                    n -> {
-                      if (n != null) {
-                        nextStream = n;
-                        next = n.spliterator();
-                      }
-                    })) {
-                  return false;
-                }
-              }
-              return true;
-            }
-          };
-
-      return new DoubleStreamSource(this, flatMapSpliterator);
-    }
-
-    @Override
-    public DoubleStream distinct() {
-      throwIfTerminated();
-      HashSet<Double> seen = new HashSet<>();
-      return filter(seen::add);
-    }
-
-    @Override
-    public DoubleStream sorted() {
-      throwIfTerminated();
-
-      Spliterator.OfDouble sortingSpliterator =
-          new Spliterators.AbstractDoubleSpliterator(
-              spliterator.estimateSize(), spliterator.characteristics() | Spliterator.SORTED) {
-            Spliterator.OfDouble ordered = null;
-
-            @Override
-            public Comparator<? super Double> getComparator() {
-              return null;
-            }
-
-            @Override
-            public boolean tryAdvance(DoubleConsumer action) {
-              if (ordered == null) {
-                double[] list = new double[0];
-                spliterator.forEachRemaining((double item) -> list[list.length] = item);
-                Arrays.sort(list);
-                ordered = Spliterators.spliterator(list, characteristics());
-              }
-              return ordered.tryAdvance(action);
-            }
-          };
-
-      return new DoubleStreamSource(this, sortingSpliterator);
-    }
-
-    @Override
-    public DoubleStream peek(DoubleConsumer action) {
-      checkNotNull(action);
-      throwIfTerminated();
-
-      Spliterator.OfDouble peekSpliterator =
-          new Spliterators.AbstractDoubleSpliterator(
-              spliterator.estimateSize(), spliterator.characteristics()) {
-            @Override
-            public boolean tryAdvance(final DoubleConsumer innerAction) {
-              return spliterator.tryAdvance(action.andThen(innerAction));
-            }
-          };
-
-      return new DoubleStreamSource(this, peekSpliterator);
-    }
-
-    @Override
-    public DoubleStream limit(long maxSize) {
-      throwIfTerminated();
-      checkState(maxSize >= 0, "maxSize may not be negative");
-      return new DoubleStreamSource(this, new LimitSpliterator(maxSize, spliterator));
-    }
-
-    @Override
-    public DoubleStream skip(long n) {
-      throwIfTerminated();
-      checkState(n >= 0, "n may not be negative");
-      if (n == 0) {
-        return this;
-      }
-      return new DoubleStreamSource(this, new SkipSpliterator(n, spliterator));
-    }
-
-    @Override
-    public Stream<Double> boxed() {
-      return mapToObj(Double::valueOf);
-    }
-
-    @Override
-    public DoubleStream sequential() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public DoubleStream parallel() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public boolean isParallel() {
-      throwIfTerminated();
-      return false;
-    }
-
-    @Override
-    public DoubleStream unordered() {
-      throwIfTerminated();
-      return this;
-    }
-  }
 }
diff --git a/user/super/com/google/gwt/emul/java/util/stream/DoubleStreamImpl.java b/user/super/com/google/gwt/emul/java/util/stream/DoubleStreamImpl.java
new file mode 100644
index 0000000..5353a05
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/util/stream/DoubleStreamImpl.java
@@ -0,0 +1,812 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package java.util.stream;
+
+import static javaemul.internal.InternalPreconditions.checkNotNull;
+import static javaemul.internal.InternalPreconditions.checkState;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.DoubleSummaryStatistics;
+import java.util.HashSet;
+import java.util.OptionalDouble;
+import java.util.PrimitiveIterator;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.DoubleBinaryOperator;
+import java.util.function.DoubleConsumer;
+import java.util.function.DoubleFunction;
+import java.util.function.DoublePredicate;
+import java.util.function.DoubleToIntFunction;
+import java.util.function.DoubleToLongFunction;
+import java.util.function.DoubleUnaryOperator;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+import java.util.function.ObjDoubleConsumer;
+import java.util.function.Supplier;
+
+/**
+ * Main implementation of DoubleStream, wrapping a single spliterator, and an optional parent
+ * stream.
+ */
+final class DoubleStreamImpl extends TerminatableStream<DoubleStreamImpl> implements DoubleStream {
+
+  /**
+   * Represents an empty stream, doing nothing for all methods.
+   */
+  static class Empty extends TerminatableStream<Empty> implements DoubleStream {
+    public Empty(TerminatableStream<?> previous) {
+      super(previous);
+    }
+
+    @Override
+    public DoubleStream filter(DoublePredicate predicate) {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public DoubleStream map(DoubleUnaryOperator mapper) {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public <U> Stream<U> mapToObj(DoubleFunction<? extends U> mapper) {
+      throwIfTerminated();
+      return new StreamImpl.Empty<U>(this);
+    }
+
+    @Override
+    public IntStream mapToInt(DoubleToIntFunction mapper) {
+      throwIfTerminated();
+      return new IntStreamImpl.Empty(this);
+    }
+
+    @Override
+    public LongStream mapToLong(DoubleToLongFunction mapper) {
+      throwIfTerminated();
+      return new LongStreamImpl.Empty(this);
+    }
+
+    @Override
+    public DoubleStream flatMap(DoubleFunction<? extends DoubleStream> mapper) {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public DoubleStream distinct() {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public DoubleStream sorted() {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public DoubleStream peek(DoubleConsumer action) {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public DoubleStream limit(long maxSize) {
+      throwIfTerminated();
+      checkState(maxSize >= 0, "maxSize may not be negative");
+      return this;
+    }
+
+    @Override
+    public DoubleStream skip(long n) {
+      throwIfTerminated();
+      checkState(n >= 0, "n may not be negative");
+      return this;
+    }
+
+    @Override
+    public void forEach(DoubleConsumer action) {
+      terminate();
+      // do nothing
+    }
+
+    @Override
+    public void forEachOrdered(DoubleConsumer action) {
+      terminate();
+      // do nothing
+    }
+
+    @Override
+    public double[] toArray() {
+      terminate();
+      return new double[0];
+    }
+
+    @Override
+    public double reduce(double identity, DoubleBinaryOperator op) {
+      terminate();
+      return identity;
+    }
+
+    @Override
+    public OptionalDouble reduce(DoubleBinaryOperator op) {
+      terminate();
+      return OptionalDouble.empty();
+    }
+
+    @Override
+    public <R> R collect(
+        Supplier<R> supplier, ObjDoubleConsumer<R> accumulator, BiConsumer<R, R> combiner) {
+      terminate();
+      return supplier.get();
+    }
+
+    @Override
+    public double sum() {
+      terminate();
+      return 0;
+    }
+
+    @Override
+    public OptionalDouble min() {
+      terminate();
+      return OptionalDouble.empty();
+    }
+
+    @Override
+    public OptionalDouble max() {
+      terminate();
+      return OptionalDouble.empty();
+    }
+
+    @Override
+    public long count() {
+      terminate();
+      return 0;
+    }
+
+    @Override
+    public OptionalDouble average() {
+      terminate();
+      return OptionalDouble.empty();
+    }
+
+    @Override
+    public DoubleSummaryStatistics summaryStatistics() {
+      terminate();
+      return new DoubleSummaryStatistics();
+    }
+
+    @Override
+    public boolean anyMatch(DoublePredicate predicate) {
+      terminate();
+      return false;
+    }
+
+    @Override
+    public boolean allMatch(DoublePredicate predicate) {
+      terminate();
+      return true;
+    }
+
+    @Override
+    public boolean noneMatch(DoublePredicate predicate) {
+      terminate();
+      return true;
+    }
+
+    @Override
+    public OptionalDouble findFirst() {
+      terminate();
+      return OptionalDouble.empty();
+    }
+
+    @Override
+    public OptionalDouble findAny() {
+      terminate();
+      return OptionalDouble.empty();
+    }
+
+    @Override
+    public Stream<Double> boxed() {
+      throwIfTerminated();
+      return new StreamImpl.Empty<Double>(this);
+    }
+
+    @Override
+    public DoubleStream sequential() {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public DoubleStream parallel() {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public PrimitiveIterator.OfDouble iterator() {
+      return Spliterators.iterator(spliterator());
+    }
+
+    @Override
+    public Spliterator.OfDouble spliterator() {
+      terminate();
+      return Spliterators.emptyDoubleSpliterator();
+    }
+
+    @Override
+    public boolean isParallel() {
+      throwIfTerminated();
+      return false;
+    }
+
+    @Override
+    public DoubleStream unordered() {
+      throwIfTerminated();
+      return this;
+    }
+  }
+
+  /**
+   * Double to Int map spliterator.
+   */
+  private static final class MapToIntSpliterator extends Spliterators.AbstractIntSpliterator {
+    private final DoubleToIntFunction map;
+    private final Spliterator.OfDouble original;
+
+    public MapToIntSpliterator(DoubleToIntFunction map, Spliterator.OfDouble original) {
+      super(
+          original.estimateSize(),
+          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
+      checkNotNull(map);
+      this.map = map;
+      this.original = original;
+    }
+
+    @Override
+    public boolean tryAdvance(final IntConsumer action) {
+      return original.tryAdvance((double u) -> action.accept(map.applyAsInt(u)));
+    }
+  }
+
+  /**
+   * Double to Object map spliterator.
+   *
+   * @param <T> the type of Object in the spliterator
+   */
+  private static final class MapToObjSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
+    private final DoubleFunction<? extends T> map;
+    private final Spliterator.OfDouble original;
+
+    public MapToObjSpliterator(DoubleFunction<? extends T> map, Spliterator.OfDouble original) {
+      super(
+          original.estimateSize(),
+          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
+      checkNotNull(map);
+      this.map = map;
+      this.original = original;
+    }
+
+    @Override
+    public boolean tryAdvance(final Consumer<? super T> action) {
+      return original.tryAdvance((double u) -> action.accept(map.apply(u)));
+    }
+  }
+
+  /**
+   * Double to Long map spliterator.
+   */
+  private static final class MapToLongSpliterator extends Spliterators.AbstractLongSpliterator {
+    private final DoubleToLongFunction map;
+    private final Spliterator.OfDouble original;
+
+    public MapToLongSpliterator(DoubleToLongFunction map, Spliterator.OfDouble original) {
+      super(
+          original.estimateSize(),
+          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
+      checkNotNull(map);
+      this.map = map;
+      this.original = original;
+    }
+
+    @Override
+    public boolean tryAdvance(final LongConsumer action) {
+      return original.tryAdvance((double u) -> action.accept(map.applyAsLong(u)));
+    }
+  }
+
+  /**
+   * Double to Double map spliterator.
+   */
+  private static final class MapToDoubleSpliterator extends Spliterators.AbstractDoubleSpliterator {
+    private final DoubleUnaryOperator map;
+    private final Spliterator.OfDouble original;
+
+    public MapToDoubleSpliterator(DoubleUnaryOperator map, Spliterator.OfDouble original) {
+      super(
+          original.estimateSize(),
+          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
+      checkNotNull(map);
+      this.map = map;
+      this.original = original;
+    }
+
+    @Override
+    public boolean tryAdvance(final DoubleConsumer action) {
+      return original.tryAdvance((double u) -> action.accept(map.applyAsDouble(u)));
+    }
+  }
+
+  /**
+   * Double filter spliterator.
+   */
+  private static final class FilterSpliterator extends Spliterators.AbstractDoubleSpliterator {
+    private final DoublePredicate filter;
+    private final Spliterator.OfDouble original;
+
+    private boolean found;
+
+    public FilterSpliterator(DoublePredicate filter, Spliterator.OfDouble original) {
+      super(original.estimateSize(), original.characteristics() & ~Spliterator.SIZED);
+      checkNotNull(filter);
+      this.filter = filter;
+      this.original = original;
+    }
+
+    @Override
+    public Comparator<? super Double> getComparator() {
+      return original.getComparator();
+    }
+
+    @Override
+    public boolean tryAdvance(final DoubleConsumer action) {
+      found = false;
+      while (!found
+          && original.tryAdvance(
+              (double item) -> {
+                if (filter.test(item)) {
+                  found = true;
+                  action.accept(item);
+                }
+              })) {
+        // do nothing, work is done in tryAdvance
+      }
+
+      return found;
+    }
+  }
+
+  /**
+   * Double skip spliterator.
+   */
+  private static final class SkipSpliterator extends Spliterators.AbstractDoubleSpliterator {
+    private long skip;
+    private final Spliterator.OfDouble original;
+
+    public SkipSpliterator(long skip, Spliterator.OfDouble original) {
+      super(
+          original.hasCharacteristics(Spliterator.SIZED)
+              ? Math.max(0, original.estimateSize() - skip)
+              : Long.MAX_VALUE,
+          original.characteristics());
+      this.skip = skip;
+      this.original = original;
+    }
+
+    @Override
+    public Comparator<? super Double> getComparator() {
+      return original.getComparator();
+    }
+
+    @Override
+    public boolean tryAdvance(DoubleConsumer action) {
+      while (skip > 0) {
+        if (!original.tryAdvance((double ignore) -> { })) {
+          return false;
+        }
+        skip--;
+      }
+      return original.tryAdvance(action);
+    }
+  }
+
+  /**
+   * Double limit spliterator.
+   */
+  private static final class LimitSpliterator extends Spliterators.AbstractDoubleSpliterator {
+    private final long limit;
+    private final Spliterator.OfDouble original;
+    private int position = 0;
+
+    public LimitSpliterator(long limit, Spliterator.OfDouble original) {
+      super(
+          original.hasCharacteristics(Spliterator.SIZED)
+              ? Math.min(original.estimateSize(), limit)
+              : Long.MAX_VALUE,
+          original.characteristics());
+      this.limit = limit;
+      this.original = original;
+    }
+
+    @Override
+    public Comparator<? super Double> getComparator() {
+      return original.getComparator();
+    }
+
+    @Override
+    public boolean tryAdvance(DoubleConsumer action) {
+      if (position >= limit) {
+        return false;
+      }
+
+      boolean result = original.tryAdvance(action);
+      position++;
+      return result;
+    }
+  }
+
+  /**
+   * Value holder for various stream operations.
+   */
+  private static class ValueConsumer implements DoubleConsumer {
+    double value;
+
+    @Override
+    public void accept(double value) {
+      this.value = value;
+    }
+  }
+
+  private final Spliterator.OfDouble spliterator;
+
+  public DoubleStreamImpl(TerminatableStream<?> previous, Spliterator.OfDouble spliterator) {
+    super(previous);
+    this.spliterator = spliterator;
+  }
+
+  // terminals
+
+  @Override
+  public void forEach(DoubleConsumer action) {
+    forEachOrdered(action);
+  }
+
+  @Override
+  public void forEachOrdered(DoubleConsumer action) {
+    terminate();
+    spliterator.forEachRemaining(action);
+  }
+
+  @Override
+  public double[] toArray() {
+    terminate();
+    double[] entries = new double[0];
+    // this is legal in js, since the array will be backed by a JS array
+    spliterator.forEachRemaining((double value) -> entries[entries.length] = value);
+
+    return entries;
+  }
+
+  @Override
+  public double reduce(double identity, DoubleBinaryOperator op) {
+    terminate();
+    ValueConsumer holder = new ValueConsumer();
+    holder.value = identity;
+    spliterator.forEachRemaining(
+        (double value) -> {
+          holder.accept(op.applyAsDouble(holder.value, value));
+        });
+    return holder.value;
+  }
+
+  @Override
+  public OptionalDouble reduce(DoubleBinaryOperator op) {
+    ValueConsumer holder = new ValueConsumer();
+    if (spliterator.tryAdvance(holder)) {
+      return OptionalDouble.of(reduce(holder.value, op));
+    }
+    terminate();
+    return OptionalDouble.empty();
+  }
+
+  @Override
+  public <R> R collect(
+      Supplier<R> supplier, ObjDoubleConsumer<R> accumulator, BiConsumer<R, R> combiner) {
+    terminate();
+    final R acc = supplier.get();
+    spliterator.forEachRemaining((double value) -> accumulator.accept(acc, value));
+    return acc;
+  }
+
+  @Override
+  public double sum() {
+    return summaryStatistics().getSum();
+  }
+
+  @Override
+  public OptionalDouble min() {
+    DoubleSummaryStatistics stats = summaryStatistics();
+    if (stats.getCount() == 0) {
+      return OptionalDouble.empty();
+    }
+    return OptionalDouble.of(stats.getMin());
+  }
+
+  @Override
+  public OptionalDouble max() {
+    DoubleSummaryStatistics stats = summaryStatistics();
+    if (stats.getCount() == 0) {
+      return OptionalDouble.empty();
+    }
+    return OptionalDouble.of(stats.getMax());
+  }
+
+  @Override
+  public long count() {
+    terminate();
+    long count = 0;
+    while (spliterator.tryAdvance((double value) -> { })) {
+      count++;
+    }
+    return count;
+  }
+
+  @Override
+  public OptionalDouble average() {
+    DoubleSummaryStatistics stats = summaryStatistics();
+    if (stats.getCount() == 0) {
+      return OptionalDouble.empty();
+    }
+    return OptionalDouble.of(stats.getAverage());
+  }
+
+  @Override
+  public DoubleSummaryStatistics summaryStatistics() {
+    return collect(
+        DoubleSummaryStatistics::new,
+        // TODO switch to a lambda reference once #9340 is fixed
+        (doubleSummaryStatistics, value) -> doubleSummaryStatistics.accept(value),
+        DoubleSummaryStatistics::combine);
+  }
+
+  @Override
+  public boolean anyMatch(DoublePredicate predicate) {
+    return filter(predicate).findFirst().isPresent();
+  }
+
+  @Override
+  public boolean allMatch(DoublePredicate predicate) {
+    return !anyMatch(predicate.negate());
+  }
+
+  @Override
+  public boolean noneMatch(DoublePredicate predicate) {
+    return !anyMatch(predicate);
+  }
+
+  @Override
+  public OptionalDouble findFirst() {
+    terminate();
+    ValueConsumer holder = new ValueConsumer();
+    if (spliterator.tryAdvance(holder)) {
+      return OptionalDouble.of(holder.value);
+    }
+    return OptionalDouble.empty();
+  }
+
+  @Override
+  public OptionalDouble findAny() {
+    return findFirst();
+  }
+
+  @Override
+  public PrimitiveIterator.OfDouble iterator() {
+    return Spliterators.iterator(spliterator());
+  }
+
+  @Override
+  public Spliterator.OfDouble spliterator() {
+    terminate();
+    return spliterator;
+  }
+
+  // end terminals
+
+  // intermediates
+
+  @Override
+  public DoubleStream filter(DoublePredicate predicate) {
+    throwIfTerminated();
+    return new DoubleStreamImpl(this, new FilterSpliterator(predicate, spliterator));
+  }
+
+  @Override
+  public DoubleStream map(DoubleUnaryOperator mapper) {
+    throwIfTerminated();
+    return new DoubleStreamImpl(this, new MapToDoubleSpliterator(mapper, spliterator));
+  }
+
+  @Override
+  public <U> Stream<U> mapToObj(DoubleFunction<? extends U> mapper) {
+    throwIfTerminated();
+    return new StreamImpl<U>(this, new MapToObjSpliterator<U>(mapper, spliterator));
+  }
+
+  @Override
+  public IntStream mapToInt(DoubleToIntFunction mapper) {
+    throwIfTerminated();
+    return new IntStreamImpl(this, new MapToIntSpliterator(mapper, spliterator));
+  }
+
+  @Override
+  public LongStream mapToLong(DoubleToLongFunction mapper) {
+    throwIfTerminated();
+    return new LongStreamImpl(this, new MapToLongSpliterator(mapper, spliterator));
+  }
+
+  @Override
+  public DoubleStream flatMap(DoubleFunction<? extends DoubleStream> mapper) {
+    throwIfTerminated();
+    final Spliterator<? extends DoubleStream> spliteratorOfStreams =
+        new MapToObjSpliterator<DoubleStream>(mapper, spliterator);
+
+    Spliterator.OfDouble flatMapSpliterator =
+        new Spliterators.AbstractDoubleSpliterator(Long.MAX_VALUE, 0) {
+          DoubleStream nextStream;
+          Spliterator.OfDouble next;
+
+          @Override
+          public boolean tryAdvance(DoubleConsumer action) {
+            // look for a new spliterator
+            while (advanceToNextSpliterator()) {
+              // if we have one, try to read and use it
+              if (next.tryAdvance(action)) {
+                return true;
+              } else {
+                nextStream.close();
+                nextStream = null;
+                // failed, null it out so we can find another
+                next = null;
+              }
+            }
+            return false;
+          }
+
+          private boolean advanceToNextSpliterator() {
+            while (next == null) {
+              if (!spliteratorOfStreams.tryAdvance(
+                  n -> {
+                    if (n != null) {
+                      nextStream = n;
+                      next = n.spliterator();
+                    }
+                  })) {
+                return false;
+              }
+            }
+            return true;
+          }
+        };
+
+    return new DoubleStreamImpl(this, flatMapSpliterator);
+  }
+
+  @Override
+  public DoubleStream distinct() {
+    throwIfTerminated();
+    HashSet<Double> seen = new HashSet<>();
+    return filter(seen::add);
+  }
+
+  @Override
+  public DoubleStream sorted() {
+    throwIfTerminated();
+
+    Spliterator.OfDouble sortingSpliterator =
+        new Spliterators.AbstractDoubleSpliterator(
+            spliterator.estimateSize(), spliterator.characteristics() | Spliterator.SORTED) {
+          Spliterator.OfDouble ordered = null;
+
+          @Override
+          public Comparator<? super Double> getComparator() {
+            return null;
+          }
+
+          @Override
+          public boolean tryAdvance(DoubleConsumer action) {
+            if (ordered == null) {
+              double[] list = new double[0];
+              spliterator.forEachRemaining((double item) -> list[list.length] = item);
+              Arrays.sort(list);
+              ordered = Spliterators.spliterator(list, characteristics());
+            }
+            return ordered.tryAdvance(action);
+          }
+        };
+
+    return new DoubleStreamImpl(this, sortingSpliterator);
+  }
+
+  @Override
+  public DoubleStream peek(DoubleConsumer action) {
+    checkNotNull(action);
+    throwIfTerminated();
+
+    Spliterator.OfDouble peekSpliterator =
+        new Spliterators.AbstractDoubleSpliterator(
+            spliterator.estimateSize(), spliterator.characteristics()) {
+          @Override
+          public boolean tryAdvance(final DoubleConsumer innerAction) {
+            return spliterator.tryAdvance(action.andThen(innerAction));
+          }
+        };
+
+    return new DoubleStreamImpl(this, peekSpliterator);
+  }
+
+  @Override
+  public DoubleStream limit(long maxSize) {
+    throwIfTerminated();
+    checkState(maxSize >= 0, "maxSize may not be negative");
+    return new DoubleStreamImpl(this, new LimitSpliterator(maxSize, spliterator));
+  }
+
+  @Override
+  public DoubleStream skip(long n) {
+    throwIfTerminated();
+    checkState(n >= 0, "n may not be negative");
+    if (n == 0) {
+      return this;
+    }
+    return new DoubleStreamImpl(this, new SkipSpliterator(n, spliterator));
+  }
+
+  @Override
+  public Stream<Double> boxed() {
+    return mapToObj(Double::valueOf);
+  }
+
+  @Override
+  public DoubleStream sequential() {
+    throwIfTerminated();
+    return this;
+  }
+
+  @Override
+  public DoubleStream parallel() {
+    throwIfTerminated();
+    return this;
+  }
+
+  @Override
+  public boolean isParallel() {
+    throwIfTerminated();
+    return false;
+  }
+
+  @Override
+  public DoubleStream unordered() {
+    throwIfTerminated();
+    return this;
+  }
+}
diff --git a/user/super/com/google/gwt/emul/java/util/stream/IntStream.java b/user/super/com/google/gwt/emul/java/util/stream/IntStream.java
index 1a2b3fd..efd9ef4 100644
--- a/user/super/com/google/gwt/emul/java/util/stream/IntStream.java
+++ b/user/super/com/google/gwt/emul/java/util/stream/IntStream.java
@@ -13,15 +13,12 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package java.util.stream;
 
-import static javaemul.internal.InternalPreconditions.checkNotNull;
 import static javaemul.internal.InternalPreconditions.checkState;
 
 import java.util.Arrays;
 import java.util.Comparator;
-import java.util.HashSet;
 import java.util.IntSummaryStatistics;
 import java.util.OptionalDouble;
 import java.util.OptionalInt;
@@ -30,8 +27,6 @@
 import java.util.Spliterators;
 import java.util.Spliterators.AbstractIntSpliterator;
 import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.function.DoubleConsumer;
 import java.util.function.IntBinaryOperator;
 import java.util.function.IntConsumer;
 import java.util.function.IntFunction;
@@ -40,7 +35,6 @@
 import java.util.function.IntToDoubleFunction;
 import java.util.function.IntToLongFunction;
 import java.util.function.IntUnaryOperator;
-import java.util.function.LongConsumer;
 import java.util.function.ObjIntConsumer;
 import java.util.function.Supplier;
 
@@ -51,18 +45,23 @@
 public interface IntStream extends BaseStream<Integer, IntStream> {
 
   /**
-   * Value holder for various stream operations.
+   * See <a
+   * href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/IntStream.Builder.html">the
+   * official Java API doc</a> for details.
    */
-  static final class ValueConsumer implements IntConsumer {
-    int value;
-
+  interface Builder extends IntConsumer {
     @Override
-    public void accept(int value) {
-      this.value = value;
+    void accept(int t);
+
+    default IntStream.Builder add(int t) {
+      accept(t);
+      return this;
     }
+
+    IntStream build();
   }
 
-  static IntStream.Builder builder() {
+  static Builder builder() {
     return new Builder() {
       private int[] items = new int[0];
 
@@ -126,7 +125,7 @@
           }
         };
 
-    IntStream result = new IntStreamSource(null, spliterator);
+    IntStream result = new IntStreamImpl(null, spliterator);
 
     result.onClose(a::close);
     result.onClose(b::close);
@@ -135,7 +134,7 @@
   }
 
   static IntStream empty() {
-    return new EmptyIntStreamSource(null);
+    return new IntStreamImpl.Empty(null);
   }
 
   static IntStream generate(final IntSupplier s) {
@@ -221,23 +220,6 @@
     return StreamSupport.intStream(spliterator, false);
   }
 
-  /**
-   * See
-   * <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/IntStream.Builder.html">
-   * the official Java API doc</a> for details.
-   */
-  public interface Builder extends IntConsumer {
-    @Override
-    void accept(int t);
-
-    default IntStream.Builder add(int t) {
-      accept(t);
-      return this;
-    }
-
-    IntStream build();
-  }
-
   boolean allMatch(IntPredicate predicate);
 
   boolean anyMatch(IntPredicate predicate);
@@ -311,781 +293,4 @@
   IntSummaryStatistics summaryStatistics();
 
   int[] toArray();
-
-  /**
-   * Represents an empty stream, doing nothing for all methods.
-   */
-  static class EmptyIntStreamSource extends TerminatableStream<EmptyIntStreamSource>
-      implements IntStream {
-    public EmptyIntStreamSource(TerminatableStream<?> previous) {
-      super(previous);
-    }
-
-    @Override
-    public IntStream filter(IntPredicate predicate) {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public IntStream map(IntUnaryOperator mapper) {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public <U> Stream<U> mapToObj(IntFunction<? extends U> mapper) {
-      throwIfTerminated();
-      return new Stream.EmptyStreamSource<U>(this);
-    }
-
-    @Override
-    public LongStream mapToLong(IntToLongFunction mapper) {
-      throwIfTerminated();
-      return new LongStream.EmptyLongStreamSource(this);
-    }
-
-    @Override
-    public DoubleStream mapToDouble(IntToDoubleFunction mapper) {
-      throwIfTerminated();
-      return new DoubleStream.EmptyDoubleStreamSource(this);
-    }
-
-    @Override
-    public IntStream flatMap(IntFunction<? extends IntStream> mapper) {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public IntStream distinct() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public IntStream sorted() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public IntStream peek(IntConsumer action) {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public IntStream limit(long maxSize) {
-      throwIfTerminated();
-      checkState(maxSize >= 0, "maxSize may not be negative");
-      return this;
-    }
-
-    @Override
-    public IntStream skip(long n) {
-      throwIfTerminated();
-      checkState(n >= 0, "n may not be negative");
-      return this;
-    }
-
-    @Override
-    public void forEach(IntConsumer action) {
-      terminate();
-      // do nothing
-    }
-
-    @Override
-    public void forEachOrdered(IntConsumer action) {
-      terminate();
-      // do nothing
-    }
-
-    @Override
-    public int[] toArray() {
-      terminate();
-      return new int[0];
-    }
-
-    @Override
-    public int reduce(int identity, IntBinaryOperator op) {
-      terminate();
-      return identity;
-    }
-
-    @Override
-    public OptionalInt reduce(IntBinaryOperator op) {
-      terminate();
-      return OptionalInt.empty();
-    }
-
-    @Override
-    public <R> R collect(
-        Supplier<R> supplier, ObjIntConsumer<R> accumulator, BiConsumer<R, R> combiner) {
-      terminate();
-      return supplier.get();
-    }
-
-    @Override
-    public int sum() {
-      terminate();
-      return 0;
-    }
-
-    @Override
-    public OptionalInt min() {
-      terminate();
-      return OptionalInt.empty();
-    }
-
-    @Override
-    public OptionalInt max() {
-      terminate();
-      return OptionalInt.empty();
-    }
-
-    @Override
-    public long count() {
-      terminate();
-      return 0;
-    }
-
-    @Override
-    public OptionalDouble average() {
-      terminate();
-      return OptionalDouble.empty();
-    }
-
-    @Override
-    public IntSummaryStatistics summaryStatistics() {
-      terminate();
-      return new IntSummaryStatistics();
-    }
-
-    @Override
-    public boolean anyMatch(IntPredicate predicate) {
-      terminate();
-      return false;
-    }
-
-    @Override
-    public boolean allMatch(IntPredicate predicate) {
-      terminate();
-      return true;
-    }
-
-    @Override
-    public boolean noneMatch(IntPredicate predicate) {
-      terminate();
-      return true;
-    }
-
-    @Override
-    public OptionalInt findFirst() {
-      terminate();
-      return OptionalInt.empty();
-    }
-
-    @Override
-    public OptionalInt findAny() {
-      terminate();
-      return OptionalInt.empty();
-    }
-
-    @Override
-    public LongStream asLongStream() {
-      throwIfTerminated();
-      return new LongStream.EmptyLongStreamSource(this);
-    }
-
-    @Override
-    public DoubleStream asDoubleStream() {
-      throwIfTerminated();
-      return new DoubleStream.EmptyDoubleStreamSource(this);
-    }
-
-    @Override
-    public Stream<Integer> boxed() {
-      throwIfTerminated();
-      return new Stream.EmptyStreamSource<Integer>(this);
-    }
-
-    @Override
-    public IntStream sequential() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public IntStream parallel() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public PrimitiveIterator.OfInt iterator() {
-      return Spliterators.iterator(spliterator());
-    }
-
-    @Override
-    public Spliterator.OfInt spliterator() {
-      terminate();
-      return Spliterators.emptyIntSpliterator();
-    }
-
-    @Override
-    public boolean isParallel() {
-      throwIfTerminated();
-      return false;
-    }
-
-    @Override
-    public IntStream unordered() {
-      throwIfTerminated();
-      return this;
-    }
-  }
-
-  /**
-   * Int to Int map spliterator.
-   */
-  static final class MapToIntSpliterator extends Spliterators.AbstractIntSpliterator {
-    private final IntUnaryOperator map;
-    private final Spliterator.OfInt original;
-
-    public MapToIntSpliterator(IntUnaryOperator map, Spliterator.OfInt original) {
-      super(
-          original.estimateSize(),
-          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
-      checkNotNull(map);
-      this.map = map;
-      this.original = original;
-    }
-
-    @Override
-    public boolean tryAdvance(final IntConsumer action) {
-      return original.tryAdvance((int u) -> action.accept(map.applyAsInt(u)));
-    }
-  }
-
-  /**
-   * Int to Object map spliterator.
-   * @param <T> the type of data in the object spliterator
-   */
-  static final class MapToObjSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
-    private final IntFunction<? extends T> map;
-    private final Spliterator.OfInt original;
-
-    public MapToObjSpliterator(IntFunction<? extends T> map, Spliterator.OfInt original) {
-      super(
-          original.estimateSize(),
-          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
-      checkNotNull(map);
-      this.map = map;
-      this.original = original;
-    }
-
-    @Override
-    public boolean tryAdvance(final Consumer<? super T> action) {
-      return original.tryAdvance((int u) -> action.accept(map.apply(u)));
-    }
-  }
-
-  /**
-   * Int to Long map spliterator.
-   */
-  static final class MapToLongSpliterator extends Spliterators.AbstractLongSpliterator {
-    private final IntToLongFunction map;
-    private final Spliterator.OfInt original;
-
-    public MapToLongSpliterator(IntToLongFunction map, Spliterator.OfInt original) {
-      super(
-          original.estimateSize(),
-          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
-      checkNotNull(map);
-      this.map = map;
-      this.original = original;
-    }
-
-    @Override
-    public boolean tryAdvance(final LongConsumer action) {
-      return original.tryAdvance((int u) -> action.accept(map.applyAsLong(u)));
-    }
-  }
-
-  /**
-   * Int to Double map spliterator.
-   */
-  static final class MapToDoubleSpliterator extends Spliterators.AbstractDoubleSpliterator {
-    private final IntToDoubleFunction map;
-    private final Spliterator.OfInt original;
-
-    public MapToDoubleSpliterator(IntToDoubleFunction map, Spliterator.OfInt original) {
-      super(
-          original.estimateSize(),
-          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
-      checkNotNull(map);
-      this.map = map;
-      this.original = original;
-    }
-
-    @Override
-    public boolean tryAdvance(final DoubleConsumer action) {
-      return original.tryAdvance((int u) -> action.accept(map.applyAsDouble(u)));
-    }
-  }
-
-  /**
-   * Int filter spliterator.
-   */
-  static final class FilterSpliterator extends Spliterators.AbstractIntSpliterator {
-    private final IntPredicate filter;
-    private final Spliterator.OfInt original;
-
-    private boolean found;
-
-    public FilterSpliterator(IntPredicate filter, Spliterator.OfInt original) {
-      super(original.estimateSize(), original.characteristics() & ~Spliterator.SIZED);
-      checkNotNull(filter);
-      this.filter = filter;
-      this.original = original;
-    }
-
-    @Override
-    public Comparator<? super Integer> getComparator() {
-      return original.getComparator();
-    }
-
-    @Override
-    public boolean tryAdvance(final IntConsumer action) {
-      found = false;
-      while (!found
-          && original.tryAdvance(
-              (int item) -> {
-                if (filter.test(item)) {
-                  found = true;
-                  action.accept(item);
-                }
-              })) {
-        // do nothing, work is done in tryAdvance
-      }
-
-      return found;
-    }
-  }
-
-  /**
-   * Int skip spliterator.
-   */
-  static final class SkipSpliterator extends Spliterators.AbstractIntSpliterator {
-    private long skip;
-    private final Spliterator.OfInt original;
-
-    public SkipSpliterator(long skip, Spliterator.OfInt original) {
-      super(
-          original.hasCharacteristics(Spliterator.SIZED)
-              ? Math.max(0, original.estimateSize() - skip)
-              : Long.MAX_VALUE,
-          original.characteristics());
-      this.skip = skip;
-      this.original = original;
-    }
-
-    @Override
-    public Comparator<? super Integer> getComparator() {
-      return original.getComparator();
-    }
-
-    @Override
-    public boolean tryAdvance(IntConsumer action) {
-      while (skip > 0) {
-        if (!original.tryAdvance((int ignore) -> { })) {
-          return false;
-        }
-        skip--;
-      }
-      return original.tryAdvance(action);
-    }
-  }
-
-  /**
-   * Int limit spliterator.
-   */
-  static final class LimitSpliterator extends Spliterators.AbstractIntSpliterator {
-    private final long limit;
-    private final Spliterator.OfInt original;
-    private int position = 0;
-
-    public LimitSpliterator(long limit, Spliterator.OfInt original) {
-      super(
-          original.hasCharacteristics(Spliterator.SIZED)
-              ? Math.min(original.estimateSize(), limit)
-              : Long.MAX_VALUE,
-          original.characteristics());
-      this.limit = limit;
-      this.original = original;
-    }
-
-    @Override
-    public Comparator<? super Integer> getComparator() {
-      return original.getComparator();
-    }
-
-    @Override
-    public boolean tryAdvance(IntConsumer action) {
-      if (position >= limit) {
-        return false;
-      }
-      boolean result = original.tryAdvance(action);
-      position++;
-      return result;
-    }
-  }
-
-  /**
-   * Main implementation of IntStream, wrapping a single spliterator, and an optional parent stream.
-   */
-  static class IntStreamSource extends TerminatableStream<IntStreamSource> implements IntStream {
-    private final Spliterator.OfInt spliterator;
-
-    public IntStreamSource(TerminatableStream<?> previous, Spliterator.OfInt spliterator) {
-      super(previous);
-      this.spliterator = spliterator;
-    }
-
-    // terminals
-    @Override
-    public Spliterator.OfInt spliterator() {
-      terminate();
-      return spliterator;
-    }
-
-    @Override
-    public PrimitiveIterator.OfInt iterator() {
-      return Spliterators.iterator(spliterator());
-    }
-
-    @Override
-    public OptionalInt findFirst() {
-      terminate();
-      ValueConsumer holder = new ValueConsumer();
-      if (spliterator.tryAdvance(holder)) {
-        return OptionalInt.of(holder.value);
-      }
-      return OptionalInt.empty();
-    }
-
-    @Override
-    public OptionalInt findAny() {
-      return findFirst();
-    }
-
-    @Override
-    public boolean noneMatch(IntPredicate predicate) {
-      return !anyMatch(predicate);
-    }
-
-    @Override
-    public boolean allMatch(IntPredicate predicate) {
-      return !anyMatch(predicate.negate());
-    }
-
-    @Override
-    public boolean anyMatch(IntPredicate predicate) {
-      return filter(predicate).findFirst().isPresent();
-    }
-
-    @Override
-    public IntSummaryStatistics summaryStatistics() {
-      return collect(
-          IntSummaryStatistics::new,
-          // TODO switch to a lambda reference once #9340 is fixed
-          (intSummaryStatistics, value) -> intSummaryStatistics.accept(value),
-          IntSummaryStatistics::combine);
-    }
-
-    @Override
-    public OptionalDouble average() {
-      IntSummaryStatistics stats = summaryStatistics();
-      if (stats.getCount() == 0) {
-        return OptionalDouble.empty();
-      }
-      return OptionalDouble.of(stats.getAverage());
-    }
-
-    @Override
-    public long count() {
-      terminate();
-      long count = 0;
-      while (spliterator.tryAdvance((int value) -> { })) {
-        count++;
-      }
-      return count;
-    }
-
-    @Override
-    public OptionalInt max() {
-      IntSummaryStatistics stats = summaryStatistics();
-      if (stats.getCount() == 0) {
-        return OptionalInt.empty();
-      }
-      return OptionalInt.of(stats.getMax());
-    }
-
-    @Override
-    public OptionalInt min() {
-      IntSummaryStatistics stats = summaryStatistics();
-      if (stats.getCount() == 0) {
-        return OptionalInt.empty();
-      }
-      return OptionalInt.of(stats.getMin());
-    }
-
-    @Override
-    public int sum() {
-      return (int) summaryStatistics().getSum();
-    }
-
-    @Override
-    public <R> R collect(
-        Supplier<R> supplier, final ObjIntConsumer<R> accumulator, BiConsumer<R, R> combiner) {
-      terminate();
-      final R acc = supplier.get();
-      spliterator.forEachRemaining((int value) -> accumulator.accept(acc, value));
-      return acc;
-    }
-
-    @Override
-    public OptionalInt reduce(IntBinaryOperator op) {
-      ValueConsumer holder = new ValueConsumer();
-      if (spliterator.tryAdvance(holder)) {
-        return OptionalInt.of(reduce(holder.value, op));
-      }
-      terminate();
-      return OptionalInt.empty();
-    }
-
-    @Override
-    public int reduce(int identity, IntBinaryOperator op) {
-      terminate();
-      ValueConsumer holder = new ValueConsumer();
-      holder.value = identity;
-      spliterator.forEachRemaining(
-          (int value) -> {
-            holder.accept(op.applyAsInt(holder.value, value));
-          });
-      return holder.value;
-    }
-
-    @Override
-    public int[] toArray() {
-      terminate();
-      int[] entries = new int[0];
-      // this is legal in js, since the array will be backed by a JS array
-      spliterator.forEachRemaining((int value) -> entries[entries.length] = value);
-
-      return entries;
-    }
-
-    @Override
-    public void forEachOrdered(IntConsumer action) {
-      terminate();
-      spliterator.forEachRemaining(action);
-    }
-
-    @Override
-    public void forEach(IntConsumer action) {
-      forEachOrdered(action);
-    }
-    // end terminals
-
-    // intermediates
-
-    @Override
-    public IntStream filter(IntPredicate predicate) {
-      throwIfTerminated();
-      return new IntStreamSource(this, new FilterSpliterator(predicate, spliterator));
-    }
-
-    @Override
-    public IntStream map(IntUnaryOperator mapper) {
-      throwIfTerminated();
-      return new IntStreamSource(this, new MapToIntSpliterator(mapper, spliterator));
-    }
-
-    @Override
-    public <U> Stream<U> mapToObj(IntFunction<? extends U> mapper) {
-      throwIfTerminated();
-      return new Stream.StreamSource<U>(this, new MapToObjSpliterator<U>(mapper, spliterator));
-    }
-
-    @Override
-    public LongStream mapToLong(IntToLongFunction mapper) {
-      throwIfTerminated();
-      return new LongStream.LongStreamSource(this, new MapToLongSpliterator(mapper, spliterator));
-    }
-
-    @Override
-    public DoubleStream mapToDouble(IntToDoubleFunction mapper) {
-      throwIfTerminated();
-      return new DoubleStream.DoubleStreamSource(
-          this, new MapToDoubleSpliterator(mapper, spliterator));
-    }
-
-    @Override
-    public IntStream flatMap(IntFunction<? extends IntStream> mapper) {
-      throwIfTerminated();
-      final Spliterator<? extends IntStream> spliteratorOfStreams =
-          new MapToObjSpliterator<>(mapper, spliterator);
-
-      Spliterator.OfInt flatMapSpliterator =
-          new Spliterators.AbstractIntSpliterator(Long.MAX_VALUE, 0) {
-            IntStream nextStream;
-            Spliterator.OfInt next;
-
-            @Override
-            public boolean tryAdvance(IntConsumer action) {
-              // look for a new spliterator
-              while (advanceToNextSpliterator()) {
-                // if we have one, try to read and use it
-                if (next.tryAdvance(action)) {
-                  return true;
-                } else {
-                  nextStream.close();
-                  nextStream = null;
-                  // failed, null it out so we can find another
-                  next = null;
-                }
-              }
-              return false;
-            }
-
-            private boolean advanceToNextSpliterator() {
-              while (next == null) {
-                if (!spliteratorOfStreams.tryAdvance(
-                    n -> {
-                      if (n != null) {
-                        nextStream = n;
-                        next = n.spliterator();
-                      }
-                    })) {
-                  return false;
-                }
-              }
-              return true;
-            }
-          };
-
-      return new IntStreamSource(this, flatMapSpliterator);
-    }
-
-    @Override
-    public IntStream distinct() {
-      throwIfTerminated();
-      HashSet<Integer> seen = new HashSet<>();
-      return filter(seen::add);
-    }
-
-    @Override
-    public IntStream sorted() {
-      throwIfTerminated();
-
-      AbstractIntSpliterator sortedSpliterator =
-          new Spliterators.AbstractIntSpliterator(
-              spliterator.estimateSize(), spliterator.characteristics() | Spliterator.SORTED) {
-            Spliterator.OfInt ordered = null;
-
-            @Override
-            public Comparator<? super Integer> getComparator() {
-              return null;
-            }
-
-            @Override
-            public boolean tryAdvance(IntConsumer action) {
-              if (ordered == null) {
-                int[] list = new int[0];
-                spliterator.forEachRemaining((int item) -> list[list.length] = item);
-                Arrays.sort(list);
-                ordered = Spliterators.spliterator(list, characteristics());
-              }
-              return ordered.tryAdvance(action);
-            }
-          };
-
-      return new IntStreamSource(this, sortedSpliterator);
-    }
-
-    @Override
-    public IntStream peek(IntConsumer action) {
-      checkNotNull(action);
-      throwIfTerminated();
-
-      AbstractIntSpliterator peekSpliterator =
-          new Spliterators.AbstractIntSpliterator(
-              spliterator.estimateSize(), spliterator.characteristics()) {
-            @Override
-            public boolean tryAdvance(final IntConsumer innerAction) {
-              return spliterator.tryAdvance(action.andThen(innerAction));
-            }
-          };
-
-      return new IntStreamSource(this, peekSpliterator);
-    }
-
-    @Override
-    public IntStream limit(long maxSize) {
-      throwIfTerminated();
-      checkState(maxSize >= 0, "maxSize may not be negative");
-      return new IntStreamSource(this, new LimitSpliterator(maxSize, spliterator));
-    }
-
-    @Override
-    public IntStream skip(long n) {
-      throwIfTerminated();
-      checkState(n >= 0, "n may not be negative");
-      if (n == 0) {
-        return this;
-      }
-      return new IntStreamSource(this, new SkipSpliterator(n, spliterator));
-    }
-
-    @Override
-    public LongStream asLongStream() {
-      return mapToLong(i -> (long) i);
-    }
-
-    @Override
-    public DoubleStream asDoubleStream() {
-      return mapToDouble(i -> (double) i);
-    }
-
-    @Override
-    public Stream<Integer> boxed() {
-      return mapToObj(Integer::valueOf);
-    }
-
-    @Override
-    public IntStream sequential() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public IntStream parallel() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public boolean isParallel() {
-      throwIfTerminated();
-      return false;
-    }
-
-    @Override
-    public IntStream unordered() {
-      throwIfTerminated();
-      return this;
-    }
-  }
 }
diff --git a/user/super/com/google/gwt/emul/java/util/stream/IntStreamImpl.java b/user/super/com/google/gwt/emul/java/util/stream/IntStreamImpl.java
new file mode 100644
index 0000000..6839314
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/util/stream/IntStreamImpl.java
@@ -0,0 +1,832 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package java.util.stream;
+
+import static javaemul.internal.InternalPreconditions.checkNotNull;
+import static javaemul.internal.InternalPreconditions.checkState;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.IntSummaryStatistics;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.PrimitiveIterator;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.Spliterators.AbstractIntSpliterator;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntBinaryOperator;
+import java.util.function.IntConsumer;
+import java.util.function.IntFunction;
+import java.util.function.IntPredicate;
+import java.util.function.IntToDoubleFunction;
+import java.util.function.IntToLongFunction;
+import java.util.function.IntUnaryOperator;
+import java.util.function.LongConsumer;
+import java.util.function.ObjIntConsumer;
+import java.util.function.Supplier;
+
+/**
+ * Main implementation of IntStream, wrapping a single spliterator, and an optional parent stream.
+ */
+final class IntStreamImpl extends TerminatableStream<IntStreamImpl> implements IntStream {
+
+  /**
+   * Represents an empty stream, doing nothing for all methods.
+   */
+  static class Empty extends TerminatableStream<Empty> implements IntStream {
+    public Empty(TerminatableStream<?> previous) {
+      super(previous);
+    }
+
+    @Override
+    public IntStream filter(IntPredicate predicate) {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public IntStream map(IntUnaryOperator mapper) {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public <U> Stream<U> mapToObj(IntFunction<? extends U> mapper) {
+      throwIfTerminated();
+      return new StreamImpl.Empty<U>(this);
+    }
+
+    @Override
+    public LongStream mapToLong(IntToLongFunction mapper) {
+      throwIfTerminated();
+      return new LongStreamImpl.Empty(this);
+    }
+
+    @Override
+    public DoubleStream mapToDouble(IntToDoubleFunction mapper) {
+      throwIfTerminated();
+      return new DoubleStreamImpl.Empty(this);
+    }
+
+    @Override
+    public IntStream flatMap(IntFunction<? extends IntStream> mapper) {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public IntStream distinct() {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public IntStream sorted() {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public IntStream peek(IntConsumer action) {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public IntStream limit(long maxSize) {
+      throwIfTerminated();
+      checkState(maxSize >= 0, "maxSize may not be negative");
+      return this;
+    }
+
+    @Override
+    public IntStream skip(long n) {
+      throwIfTerminated();
+      checkState(n >= 0, "n may not be negative");
+      return this;
+    }
+
+    @Override
+    public void forEach(IntConsumer action) {
+      terminate();
+      // do nothing
+    }
+
+    @Override
+    public void forEachOrdered(IntConsumer action) {
+      terminate();
+      // do nothing
+    }
+
+    @Override
+    public int[] toArray() {
+      terminate();
+      return new int[0];
+    }
+
+    @Override
+    public int reduce(int identity, IntBinaryOperator op) {
+      terminate();
+      return identity;
+    }
+
+    @Override
+    public OptionalInt reduce(IntBinaryOperator op) {
+      terminate();
+      return OptionalInt.empty();
+    }
+
+    @Override
+    public <R> R collect(
+        Supplier<R> supplier, ObjIntConsumer<R> accumulator, BiConsumer<R, R> combiner) {
+      terminate();
+      return supplier.get();
+    }
+
+    @Override
+    public int sum() {
+      terminate();
+      return 0;
+    }
+
+    @Override
+    public OptionalInt min() {
+      terminate();
+      return OptionalInt.empty();
+    }
+
+    @Override
+    public OptionalInt max() {
+      terminate();
+      return OptionalInt.empty();
+    }
+
+    @Override
+    public long count() {
+      terminate();
+      return 0;
+    }
+
+    @Override
+    public OptionalDouble average() {
+      terminate();
+      return OptionalDouble.empty();
+    }
+
+    @Override
+    public IntSummaryStatistics summaryStatistics() {
+      terminate();
+      return new IntSummaryStatistics();
+    }
+
+    @Override
+    public boolean anyMatch(IntPredicate predicate) {
+      terminate();
+      return false;
+    }
+
+    @Override
+    public boolean allMatch(IntPredicate predicate) {
+      terminate();
+      return true;
+    }
+
+    @Override
+    public boolean noneMatch(IntPredicate predicate) {
+      terminate();
+      return true;
+    }
+
+    @Override
+    public OptionalInt findFirst() {
+      terminate();
+      return OptionalInt.empty();
+    }
+
+    @Override
+    public OptionalInt findAny() {
+      terminate();
+      return OptionalInt.empty();
+    }
+
+    @Override
+    public LongStream asLongStream() {
+      throwIfTerminated();
+      return new LongStreamImpl.Empty(this);
+    }
+
+    @Override
+    public DoubleStream asDoubleStream() {
+      throwIfTerminated();
+      return new DoubleStreamImpl.Empty(this);
+    }
+
+    @Override
+    public Stream<Integer> boxed() {
+      throwIfTerminated();
+      return new StreamImpl.Empty<Integer>(this);
+    }
+
+    @Override
+    public IntStream sequential() {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public IntStream parallel() {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public PrimitiveIterator.OfInt iterator() {
+      return Spliterators.iterator(spliterator());
+    }
+
+    @Override
+    public Spliterator.OfInt spliterator() {
+      terminate();
+      return Spliterators.emptyIntSpliterator();
+    }
+
+    @Override
+    public boolean isParallel() {
+      throwIfTerminated();
+      return false;
+    }
+
+    @Override
+    public IntStream unordered() {
+      throwIfTerminated();
+      return this;
+    }
+  }
+
+  /**
+   * Int to Int map spliterator.
+   */
+  private static final class MapToIntSpliterator extends Spliterators.AbstractIntSpliterator {
+    private final IntUnaryOperator map;
+    private final Spliterator.OfInt original;
+
+    public MapToIntSpliterator(IntUnaryOperator map, Spliterator.OfInt original) {
+      super(
+          original.estimateSize(),
+          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
+      checkNotNull(map);
+      this.map = map;
+      this.original = original;
+    }
+
+    @Override
+    public boolean tryAdvance(final IntConsumer action) {
+      return original.tryAdvance((int u) -> action.accept(map.applyAsInt(u)));
+    }
+  }
+
+  /**
+   * Int to Object map spliterator.
+   *
+   * @param <T> the type of data in the object spliterator
+   */
+  private static final class MapToObjSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
+    private final IntFunction<? extends T> map;
+    private final Spliterator.OfInt original;
+
+    public MapToObjSpliterator(IntFunction<? extends T> map, Spliterator.OfInt original) {
+      super(
+          original.estimateSize(),
+          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
+      checkNotNull(map);
+      this.map = map;
+      this.original = original;
+    }
+
+    @Override
+    public boolean tryAdvance(final Consumer<? super T> action) {
+      return original.tryAdvance((int u) -> action.accept(map.apply(u)));
+    }
+  }
+
+  /**
+   * Int to Long map spliterator.
+   */
+  private static final class MapToLongSpliterator extends Spliterators.AbstractLongSpliterator {
+    private final IntToLongFunction map;
+    private final Spliterator.OfInt original;
+
+    public MapToLongSpliterator(IntToLongFunction map, Spliterator.OfInt original) {
+      super(
+          original.estimateSize(),
+          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
+      checkNotNull(map);
+      this.map = map;
+      this.original = original;
+    }
+
+    @Override
+    public boolean tryAdvance(final LongConsumer action) {
+      return original.tryAdvance((int u) -> action.accept(map.applyAsLong(u)));
+    }
+  }
+
+  /**
+   * Int to Double map spliterator.
+   */
+  private static final class MapToDoubleSpliterator extends Spliterators.AbstractDoubleSpliterator {
+    private final IntToDoubleFunction map;
+    private final Spliterator.OfInt original;
+
+    public MapToDoubleSpliterator(IntToDoubleFunction map, Spliterator.OfInt original) {
+      super(
+          original.estimateSize(),
+          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
+      checkNotNull(map);
+      this.map = map;
+      this.original = original;
+    }
+
+    @Override
+    public boolean tryAdvance(final DoubleConsumer action) {
+      return original.tryAdvance((int u) -> action.accept(map.applyAsDouble(u)));
+    }
+  }
+
+  /**
+   * Int filter spliterator.
+   */
+  private static final class FilterSpliterator extends Spliterators.AbstractIntSpliterator {
+    private final IntPredicate filter;
+    private final Spliterator.OfInt original;
+
+    private boolean found;
+
+    public FilterSpliterator(IntPredicate filter, Spliterator.OfInt original) {
+      super(original.estimateSize(), original.characteristics() & ~Spliterator.SIZED);
+      checkNotNull(filter);
+      this.filter = filter;
+      this.original = original;
+    }
+
+    @Override
+    public Comparator<? super Integer> getComparator() {
+      return original.getComparator();
+    }
+
+    @Override
+    public boolean tryAdvance(final IntConsumer action) {
+      found = false;
+      while (!found
+          && original.tryAdvance(
+              (int item) -> {
+                if (filter.test(item)) {
+                  found = true;
+                  action.accept(item);
+                }
+              })) {
+        // do nothing, work is done in tryAdvance
+      }
+
+      return found;
+    }
+  }
+
+  /**
+   * Int skip spliterator.
+   */
+  private static final class SkipSpliterator extends Spliterators.AbstractIntSpliterator {
+    private long skip;
+    private final Spliterator.OfInt original;
+
+    public SkipSpliterator(long skip, Spliterator.OfInt original) {
+      super(
+          original.hasCharacteristics(Spliterator.SIZED)
+              ? Math.max(0, original.estimateSize() - skip)
+              : Long.MAX_VALUE,
+          original.characteristics());
+      this.skip = skip;
+      this.original = original;
+    }
+
+    @Override
+    public Comparator<? super Integer> getComparator() {
+      return original.getComparator();
+    }
+
+    @Override
+    public boolean tryAdvance(IntConsumer action) {
+      while (skip > 0) {
+        if (!original.tryAdvance((int ignore) -> { })) {
+          return false;
+        }
+        skip--;
+      }
+      return original.tryAdvance(action);
+    }
+  }
+
+  /**
+   * Int limit spliterator.
+   */
+  private static final class LimitSpliterator extends Spliterators.AbstractIntSpliterator {
+    private final long limit;
+    private final Spliterator.OfInt original;
+    private int position = 0;
+
+    public LimitSpliterator(long limit, Spliterator.OfInt original) {
+      super(
+          original.hasCharacteristics(Spliterator.SIZED)
+              ? Math.min(original.estimateSize(), limit)
+              : Long.MAX_VALUE,
+          original.characteristics());
+      this.limit = limit;
+      this.original = original;
+    }
+
+    @Override
+    public Comparator<? super Integer> getComparator() {
+      return original.getComparator();
+    }
+
+    @Override
+    public boolean tryAdvance(IntConsumer action) {
+      if (position >= limit) {
+        return false;
+      }
+      boolean result = original.tryAdvance(action);
+      position++;
+      return result;
+    }
+  }
+
+  /**
+   * Value holder for various stream operations.
+   */
+  private static final class ValueConsumer implements IntConsumer {
+    int value;
+
+    @Override
+    public void accept(int value) {
+      this.value = value;
+    }
+  }
+
+  private final Spliterator.OfInt spliterator;
+
+  public IntStreamImpl(TerminatableStream<?> previous, Spliterator.OfInt spliterator) {
+    super(previous);
+    this.spliterator = spliterator;
+  }
+
+  // terminals
+  @Override
+  public Spliterator.OfInt spliterator() {
+    terminate();
+    return spliterator;
+  }
+
+  @Override
+  public PrimitiveIterator.OfInt iterator() {
+    return Spliterators.iterator(spliterator());
+  }
+
+  @Override
+  public OptionalInt findFirst() {
+    terminate();
+    ValueConsumer holder = new ValueConsumer();
+    if (spliterator.tryAdvance(holder)) {
+      return OptionalInt.of(holder.value);
+    }
+    return OptionalInt.empty();
+  }
+
+  @Override
+  public OptionalInt findAny() {
+    return findFirst();
+  }
+
+  @Override
+  public boolean noneMatch(IntPredicate predicate) {
+    return !anyMatch(predicate);
+  }
+
+  @Override
+  public boolean allMatch(IntPredicate predicate) {
+    return !anyMatch(predicate.negate());
+  }
+
+  @Override
+  public boolean anyMatch(IntPredicate predicate) {
+    return filter(predicate).findFirst().isPresent();
+  }
+
+  @Override
+  public IntSummaryStatistics summaryStatistics() {
+    return collect(
+        IntSummaryStatistics::new,
+        // TODO switch to a lambda reference once #9340 is fixed
+        (intSummaryStatistics, value) -> intSummaryStatistics.accept(value),
+        IntSummaryStatistics::combine);
+  }
+
+  @Override
+  public OptionalDouble average() {
+    IntSummaryStatistics stats = summaryStatistics();
+    if (stats.getCount() == 0) {
+      return OptionalDouble.empty();
+    }
+    return OptionalDouble.of(stats.getAverage());
+  }
+
+  @Override
+  public long count() {
+    terminate();
+    long count = 0;
+    while (spliterator.tryAdvance((int value) -> { })) {
+      count++;
+    }
+    return count;
+  }
+
+  @Override
+  public OptionalInt max() {
+    IntSummaryStatistics stats = summaryStatistics();
+    if (stats.getCount() == 0) {
+      return OptionalInt.empty();
+    }
+    return OptionalInt.of(stats.getMax());
+  }
+
+  @Override
+  public OptionalInt min() {
+    IntSummaryStatistics stats = summaryStatistics();
+    if (stats.getCount() == 0) {
+      return OptionalInt.empty();
+    }
+    return OptionalInt.of(stats.getMin());
+  }
+
+  @Override
+  public int sum() {
+    return (int) summaryStatistics().getSum();
+  }
+
+  @Override
+  public <R> R collect(
+      Supplier<R> supplier, final ObjIntConsumer<R> accumulator, BiConsumer<R, R> combiner) {
+    terminate();
+    final R acc = supplier.get();
+    spliterator.forEachRemaining((int value) -> accumulator.accept(acc, value));
+    return acc;
+  }
+
+  @Override
+  public OptionalInt reduce(IntBinaryOperator op) {
+    ValueConsumer holder = new ValueConsumer();
+    if (spliterator.tryAdvance(holder)) {
+      return OptionalInt.of(reduce(holder.value, op));
+    }
+    terminate();
+    return OptionalInt.empty();
+  }
+
+  @Override
+  public int reduce(int identity, IntBinaryOperator op) {
+    terminate();
+    ValueConsumer holder = new ValueConsumer();
+    holder.value = identity;
+    spliterator.forEachRemaining(
+        (int value) -> {
+          holder.accept(op.applyAsInt(holder.value, value));
+        });
+    return holder.value;
+  }
+
+  @Override
+  public int[] toArray() {
+    terminate();
+    int[] entries = new int[0];
+    // this is legal in js, since the array will be backed by a JS array
+    spliterator.forEachRemaining((int value) -> entries[entries.length] = value);
+
+    return entries;
+  }
+
+  @Override
+  public void forEachOrdered(IntConsumer action) {
+    terminate();
+    spliterator.forEachRemaining(action);
+  }
+
+  @Override
+  public void forEach(IntConsumer action) {
+    forEachOrdered(action);
+  }
+  // end terminals
+
+  // intermediates
+
+  @Override
+  public IntStream filter(IntPredicate predicate) {
+    throwIfTerminated();
+    return new IntStreamImpl(this, new FilterSpliterator(predicate, spliterator));
+  }
+
+  @Override
+  public IntStream map(IntUnaryOperator mapper) {
+    throwIfTerminated();
+    return new IntStreamImpl(this, new MapToIntSpliterator(mapper, spliterator));
+  }
+
+  @Override
+  public <U> Stream<U> mapToObj(IntFunction<? extends U> mapper) {
+    throwIfTerminated();
+    return new StreamImpl<U>(this, new MapToObjSpliterator<U>(mapper, spliterator));
+  }
+
+  @Override
+  public LongStream mapToLong(IntToLongFunction mapper) {
+    throwIfTerminated();
+    return new LongStreamImpl(this, new MapToLongSpliterator(mapper, spliterator));
+  }
+
+  @Override
+  public DoubleStream mapToDouble(IntToDoubleFunction mapper) {
+    throwIfTerminated();
+    return new DoubleStreamImpl(this, new MapToDoubleSpliterator(mapper, spliterator));
+  }
+
+  @Override
+  public IntStream flatMap(IntFunction<? extends IntStream> mapper) {
+    throwIfTerminated();
+    final Spliterator<? extends IntStream> spliteratorOfStreams =
+        new MapToObjSpliterator<>(mapper, spliterator);
+
+    Spliterator.OfInt flatMapSpliterator =
+        new Spliterators.AbstractIntSpliterator(Long.MAX_VALUE, 0) {
+          IntStream nextStream;
+          Spliterator.OfInt next;
+
+          @Override
+          public boolean tryAdvance(IntConsumer action) {
+            // look for a new spliterator
+            while (advanceToNextSpliterator()) {
+              // if we have one, try to read and use it
+              if (next.tryAdvance(action)) {
+                return true;
+              } else {
+                nextStream.close();
+                nextStream = null;
+                // failed, null it out so we can find another
+                next = null;
+              }
+            }
+            return false;
+          }
+
+          private boolean advanceToNextSpliterator() {
+            while (next == null) {
+              if (!spliteratorOfStreams.tryAdvance(
+                  n -> {
+                    if (n != null) {
+                      nextStream = n;
+                      next = n.spliterator();
+                    }
+                  })) {
+                return false;
+              }
+            }
+            return true;
+          }
+        };
+
+    return new IntStreamImpl(this, flatMapSpliterator);
+  }
+
+  @Override
+  public IntStream distinct() {
+    throwIfTerminated();
+    HashSet<Integer> seen = new HashSet<>();
+    return filter(seen::add);
+  }
+
+  @Override
+  public IntStream sorted() {
+    throwIfTerminated();
+
+    AbstractIntSpliterator sortedSpliterator =
+        new Spliterators.AbstractIntSpliterator(
+            spliterator.estimateSize(), spliterator.characteristics() | Spliterator.SORTED) {
+          Spliterator.OfInt ordered = null;
+
+          @Override
+          public Comparator<? super Integer> getComparator() {
+            return null;
+          }
+
+          @Override
+          public boolean tryAdvance(IntConsumer action) {
+            if (ordered == null) {
+              int[] list = new int[0];
+              spliterator.forEachRemaining((int item) -> list[list.length] = item);
+              Arrays.sort(list);
+              ordered = Spliterators.spliterator(list, characteristics());
+            }
+            return ordered.tryAdvance(action);
+          }
+        };
+
+    return new IntStreamImpl(this, sortedSpliterator);
+  }
+
+  @Override
+  public IntStream peek(IntConsumer action) {
+    checkNotNull(action);
+    throwIfTerminated();
+
+    AbstractIntSpliterator peekSpliterator =
+        new Spliterators.AbstractIntSpliterator(
+            spliterator.estimateSize(), spliterator.characteristics()) {
+          @Override
+          public boolean tryAdvance(final IntConsumer innerAction) {
+            return spliterator.tryAdvance(action.andThen(innerAction));
+          }
+        };
+
+    return new IntStreamImpl(this, peekSpliterator);
+  }
+
+  @Override
+  public IntStream limit(long maxSize) {
+    throwIfTerminated();
+    checkState(maxSize >= 0, "maxSize may not be negative");
+    return new IntStreamImpl(this, new LimitSpliterator(maxSize, spliterator));
+  }
+
+  @Override
+  public IntStream skip(long n) {
+    throwIfTerminated();
+    checkState(n >= 0, "n may not be negative");
+    if (n == 0) {
+      return this;
+    }
+    return new IntStreamImpl(this, new SkipSpliterator(n, spliterator));
+  }
+
+  @Override
+  public LongStream asLongStream() {
+    return mapToLong(i -> (long) i);
+  }
+
+  @Override
+  public DoubleStream asDoubleStream() {
+    return mapToDouble(i -> (double) i);
+  }
+
+  @Override
+  public Stream<Integer> boxed() {
+    return mapToObj(Integer::valueOf);
+  }
+
+  @Override
+  public IntStream sequential() {
+    throwIfTerminated();
+    return this;
+  }
+
+  @Override
+  public IntStream parallel() {
+    throwIfTerminated();
+    return this;
+  }
+
+  @Override
+  public boolean isParallel() {
+    throwIfTerminated();
+    return false;
+  }
+
+  @Override
+  public IntStream unordered() {
+    throwIfTerminated();
+    return this;
+  }
+}
diff --git a/user/super/com/google/gwt/emul/java/util/stream/LongStream.java b/user/super/com/google/gwt/emul/java/util/stream/LongStream.java
index 6981225..611ef9b 100644
--- a/user/super/com/google/gwt/emul/java/util/stream/LongStream.java
+++ b/user/super/com/google/gwt/emul/java/util/stream/LongStream.java
@@ -13,15 +13,12 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package java.util.stream;
 
-import static javaemul.internal.InternalPreconditions.checkNotNull;
 import static javaemul.internal.InternalPreconditions.checkState;
 
 import java.util.Arrays;
 import java.util.Comparator;
-import java.util.HashSet;
 import java.util.LongSummaryStatistics;
 import java.util.OptionalDouble;
 import java.util.OptionalLong;
@@ -30,9 +27,6 @@
 import java.util.Spliterators;
 import java.util.Spliterators.AbstractLongSpliterator;
 import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.function.DoubleConsumer;
-import java.util.function.IntConsumer;
 import java.util.function.LongBinaryOperator;
 import java.util.function.LongConsumer;
 import java.util.function.LongFunction;
@@ -51,18 +45,23 @@
 public interface LongStream extends BaseStream<Long, LongStream> {
 
   /**
-   * Value holder for various stream operations.
+   * See <a
+   * href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/LongStream.Builder.html">the
+   * official Java API doc</a> for details.
    */
-  static final class ValueConsumer implements LongConsumer {
-    long value;
-
+  interface Builder extends LongConsumer {
     @Override
-    public void accept(long value) {
-      this.value = value;
+    void accept(long t);
+
+    default LongStream.Builder add(long t) {
+      accept(t);
+      return this;
     }
+
+    LongStream build();
   }
 
-  static LongStream.Builder builder() {
+  static Builder builder() {
     return new Builder() {
       private long[] items = new long[0];
 
@@ -126,7 +125,7 @@
           }
         };
 
-    LongStream result = new LongStreamSource(null, spliterator);
+    LongStream result = new LongStreamImpl(null, spliterator);
 
     result.onClose(a::close);
     result.onClose(b::close);
@@ -135,7 +134,7 @@
   }
 
   static LongStream empty() {
-    return new EmptyLongStreamSource(null);
+    return new LongStreamImpl.Empty(null);
   }
 
   static LongStream generate(LongSupplier s) {
@@ -219,23 +218,6 @@
     return StreamSupport.longStream(spliterator, false);
   }
 
-  /**
-   * See
-   * <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/LongStream.Builder.html">
-   * the official Java API doc</a> for details.
-   */
-  public interface Builder extends LongConsumer {
-    @Override
-    void accept(long t);
-
-    default LongStream.Builder add(long t) {
-      accept(t);
-      return this;
-    }
-
-    LongStream build();
-  }
-
   boolean allMatch(LongPredicate predicate);
 
   boolean anyMatch(LongPredicate predicate);
@@ -307,769 +289,4 @@
   LongSummaryStatistics summaryStatistics();
 
   long[] toArray();
-
-  /**
-   * Represents an empty stream, doing nothing for all methods.
-   */
-  static class EmptyLongStreamSource extends TerminatableStream<EmptyLongStreamSource>
-      implements LongStream {
-    public EmptyLongStreamSource(TerminatableStream<?> previous) {
-      super(previous);
-    }
-
-    @Override
-    public LongStream filter(LongPredicate predicate) {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public LongStream map(LongUnaryOperator mapper) {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public <U> Stream<U> mapToObj(LongFunction<? extends U> mapper) {
-      throwIfTerminated();
-      return new Stream.EmptyStreamSource<U>(this);
-    }
-
-    @Override
-    public IntStream mapToInt(LongToIntFunction mapper) {
-      throwIfTerminated();
-      return new IntStream.EmptyIntStreamSource(this);
-    }
-
-    @Override
-    public DoubleStream mapToDouble(LongToDoubleFunction mapper) {
-      throwIfTerminated();
-      return new DoubleStream.EmptyDoubleStreamSource(this);
-    }
-
-    @Override
-    public LongStream flatMap(LongFunction<? extends LongStream> mapper) {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public LongStream distinct() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public LongStream sorted() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public LongStream peek(LongConsumer action) {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public LongStream limit(long maxSize) {
-      throwIfTerminated();
-      checkState(maxSize >= 0, "maxSize may not be negative");
-      return this;
-    }
-
-    @Override
-    public LongStream skip(long n) {
-      throwIfTerminated();
-      checkState(n >= 0, "n may not be negative");
-      return this;
-    }
-
-    @Override
-    public void forEach(LongConsumer action) {
-      terminate();
-    }
-
-    @Override
-    public void forEachOrdered(LongConsumer action) {
-      terminate();
-    }
-
-    @Override
-    public long[] toArray() {
-      terminate();
-      return new long[0];
-    }
-
-    @Override
-    public long reduce(long identity, LongBinaryOperator op) {
-      terminate();
-      return identity;
-    }
-
-    @Override
-    public OptionalLong reduce(LongBinaryOperator op) {
-      terminate();
-      return OptionalLong.empty();
-    }
-
-    @Override
-    public <R> R collect(
-        Supplier<R> supplier, ObjLongConsumer<R> accumulator, BiConsumer<R, R> combiner) {
-      terminate();
-      return supplier.get();
-    }
-
-    @Override
-    public long sum() {
-      terminate();
-      return 0;
-    }
-
-    @Override
-    public OptionalLong min() {
-      terminate();
-      return OptionalLong.empty();
-    }
-
-    @Override
-    public OptionalLong max() {
-      terminate();
-      return OptionalLong.empty();
-    }
-
-    @Override
-    public long count() {
-      terminate();
-      return 0;
-    }
-
-    @Override
-    public OptionalDouble average() {
-      terminate();
-      return OptionalDouble.empty();
-    }
-
-    @Override
-    public LongSummaryStatistics summaryStatistics() {
-      terminate();
-      return new LongSummaryStatistics();
-    }
-
-    @Override
-    public boolean anyMatch(LongPredicate predicate) {
-      terminate();
-      return false;
-    }
-
-    @Override
-    public boolean allMatch(LongPredicate predicate) {
-      terminate();
-      return true;
-    }
-
-    @Override
-    public boolean noneMatch(LongPredicate predicate) {
-      terminate();
-      return true;
-    }
-
-    @Override
-    public OptionalLong findFirst() {
-      terminate();
-      return OptionalLong.empty();
-    }
-
-    @Override
-    public OptionalLong findAny() {
-      terminate();
-      return OptionalLong.empty();
-    }
-
-    @Override
-    public DoubleStream asDoubleStream() {
-      throwIfTerminated();
-      return new DoubleStream.EmptyDoubleStreamSource(this);
-    }
-
-    @Override
-    public Stream<Long> boxed() {
-      throwIfTerminated();
-      return new Stream.EmptyStreamSource<Long>(this);
-    }
-
-    @Override
-    public LongStream sequential() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public LongStream parallel() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public PrimitiveIterator.OfLong iterator() {
-      return Spliterators.iterator(spliterator());
-    }
-
-    @Override
-    public Spliterator.OfLong spliterator() {
-      terminate();
-      return Spliterators.emptyLongSpliterator();
-    }
-
-    @Override
-    public boolean isParallel() {
-      throwIfTerminated();
-      return false;
-    }
-
-    @Override
-    public LongStream unordered() {
-      throwIfTerminated();
-      return this;
-    }
-  }
-
-  /**
-   * Long to Int map spliterator.
-   */
-  static final class MapToIntSpliterator extends Spliterators.AbstractIntSpliterator {
-    private final LongToIntFunction map;
-    private final Spliterator.OfLong original;
-
-    public MapToIntSpliterator(LongToIntFunction map, Spliterator.OfLong original) {
-      super(
-          original.estimateSize(),
-          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
-      checkNotNull(map);
-      this.map = map;
-      this.original = original;
-    }
-
-    @Override
-    public boolean tryAdvance(final IntConsumer action) {
-      return original.tryAdvance((long u) -> action.accept(map.applyAsInt(u)));
-    }
-  }
-
-  /**
-   * Long to Object map spliterator.
-   * @param <T> the type of data in the object spliterator
-   */
-  static final class MapToObjSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
-    private final LongFunction<? extends T> map;
-    private final Spliterator.OfLong original;
-
-    public MapToObjSpliterator(LongFunction<? extends T> map, Spliterator.OfLong original) {
-      super(
-          original.estimateSize(),
-          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
-      checkNotNull(map);
-      this.map = map;
-      this.original = original;
-    }
-
-    @Override
-    public boolean tryAdvance(final Consumer<? super T> action) {
-      return original.tryAdvance((long u) -> action.accept(map.apply(u)));
-    }
-  }
-
-  /**
-   * Long to Long map spliterator.
-   */
-  static final class MapToLongSpliterator extends Spliterators.AbstractLongSpliterator {
-    private final LongUnaryOperator map;
-    private final Spliterator.OfLong original;
-
-    public MapToLongSpliterator(LongUnaryOperator map, Spliterator.OfLong original) {
-      super(
-          original.estimateSize(),
-          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
-      checkNotNull(map);
-      this.map = map;
-      this.original = original;
-    }
-
-    @Override
-    public boolean tryAdvance(final LongConsumer action) {
-      return original.tryAdvance((long u) -> action.accept(map.applyAsLong(u)));
-    }
-  }
-
-  /**
-   * Long to Double map Spliterator.
-   */
-  static final class MapToDoubleSpliterator extends Spliterators.AbstractDoubleSpliterator {
-    private final LongToDoubleFunction map;
-    private final Spliterator.OfLong original;
-
-    public MapToDoubleSpliterator(LongToDoubleFunction map, Spliterator.OfLong original) {
-      super(
-          original.estimateSize(),
-          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
-      checkNotNull(map);
-      this.map = map;
-      this.original = original;
-    }
-
-    @Override
-    public boolean tryAdvance(final DoubleConsumer action) {
-      return original.tryAdvance((long u) -> action.accept(map.applyAsDouble(u)));
-    }
-  }
-
-  /**
-   * Long filter spliterator.
-   */
-  static final class FilterSpliterator extends Spliterators.AbstractLongSpliterator {
-    private final LongPredicate filter;
-    private final Spliterator.OfLong original;
-
-    private boolean found;
-
-    public FilterSpliterator(LongPredicate filter, Spliterator.OfLong original) {
-      super(original.estimateSize(), original.characteristics() & ~Spliterator.SIZED);
-      checkNotNull(filter);
-      this.filter = filter;
-      this.original = original;
-    }
-
-    @Override
-    public Comparator<? super Long> getComparator() {
-      return original.getComparator();
-    }
-
-    @Override
-    public boolean tryAdvance(final LongConsumer action) {
-      found = false;
-      while (!found
-          && original.tryAdvance(
-              (long item) -> {
-                if (filter.test(item)) {
-                  found = true;
-                  action.accept(item);
-                }
-              })) {
-        // do nothing, work is done in tryAdvance
-      }
-
-      return found;
-    }
-  }
-
-  /**
-   * Long skip spliterator.
-   */
-  static final class SkipSpliterator extends Spliterators.AbstractLongSpliterator {
-    private long skip;
-    private final Spliterator.OfLong original;
-
-    public SkipSpliterator(long skip, Spliterator.OfLong original) {
-      super(
-          original.hasCharacteristics(Spliterator.SIZED)
-              ? Math.max(0, original.estimateSize() - skip)
-              : Long.MAX_VALUE,
-          original.characteristics());
-      this.skip = skip;
-      this.original = original;
-    }
-
-    @Override
-    public Comparator<? super Long> getComparator() {
-      return original.getComparator();
-    }
-
-    @Override
-    public boolean tryAdvance(LongConsumer action) {
-      while (skip > 0) {
-        if (!original.tryAdvance((long ignore) -> { })) {
-          return false;
-        }
-        skip--;
-      }
-      return original.tryAdvance(action);
-    }
-  }
-
-  /**
-   * Long limit spliterator.
-   */
-  static final class LimitSpliterator extends Spliterators.AbstractLongSpliterator {
-    private final long limit;
-    private final Spliterator.OfLong original;
-    private int position = 0;
-
-    public LimitSpliterator(long limit, Spliterator.OfLong original) {
-      super(
-          original.hasCharacteristics(Spliterator.SIZED)
-              ? Math.min(original.estimateSize(), limit)
-              : Long.MAX_VALUE,
-          original.characteristics());
-      this.limit = limit;
-      this.original = original;
-    }
-
-    @Override
-    public Comparator<? super Long> getComparator() {
-      return original.getComparator();
-    }
-
-    @Override
-    public boolean tryAdvance(LongConsumer action) {
-      if (position >= limit) {
-        return false;
-      }
-      boolean result = original.tryAdvance(action);
-      position++;
-      return result;
-    }
-  }
-
-  /**
-   * Main implementation of LongStream, wrapping a single spliterator, and an optional parent
-   * stream.
-   */
-  static class LongStreamSource extends TerminatableStream<LongStreamSource> implements LongStream {
-    private final Spliterator.OfLong spliterator;
-
-    public LongStreamSource(TerminatableStream<?> previous, Spliterator.OfLong spliterator) {
-      super(previous);
-      this.spliterator = spliterator;
-    }
-
-    // terminals
-
-    @Override
-    public void forEach(LongConsumer action) {
-      forEachOrdered(action);
-    }
-
-    @Override
-    public void forEachOrdered(LongConsumer action) {
-      terminate();
-      spliterator.forEachRemaining(action);
-    }
-
-    @Override
-    public long[] toArray() {
-      terminate();
-      long[] entries = new long[0];
-      // this is legal in js, since the array will be backed by a JS array
-      spliterator.forEachRemaining((long value) -> entries[entries.length] = value);
-
-      return entries;
-    }
-
-    @Override
-    public long reduce(long identity, LongBinaryOperator op) {
-      terminate();
-      ValueConsumer holder = new ValueConsumer();
-      holder.value = identity;
-      spliterator.forEachRemaining(
-          (long value) -> {
-            holder.accept(op.applyAsLong(holder.value, value));
-          });
-      return holder.value;
-    }
-
-    @Override
-    public OptionalLong reduce(LongBinaryOperator op) {
-      ValueConsumer holder = new ValueConsumer();
-      if (spliterator.tryAdvance(holder)) {
-        return OptionalLong.of(reduce(holder.value, op));
-      }
-      terminate();
-      return OptionalLong.empty();
-    }
-
-    @Override
-    public <R> R collect(
-        Supplier<R> supplier, ObjLongConsumer<R> accumulator, BiConsumer<R, R> combiner) {
-      terminate();
-      final R acc = supplier.get();
-      spliterator.forEachRemaining((long value) -> accumulator.accept(acc, value));
-      return acc;
-    }
-
-    @Override
-    public long sum() {
-      return summaryStatistics().getSum();
-    }
-
-    @Override
-    public OptionalLong min() {
-      LongSummaryStatistics stats = summaryStatistics();
-      if (stats.getCount() == 0) {
-        return OptionalLong.empty();
-      }
-      return OptionalLong.of(stats.getMin());
-    }
-
-    @Override
-    public OptionalLong max() {
-      LongSummaryStatistics stats = summaryStatistics();
-      if (stats.getCount() == 0) {
-        return OptionalLong.empty();
-      }
-      return OptionalLong.of(stats.getMax());
-    }
-
-    @Override
-    public long count() {
-      terminate();
-      long count = 0;
-      while (spliterator.tryAdvance((long value) -> { })) {
-        count++;
-      }
-      return count;
-    }
-
-    @Override
-    public OptionalDouble average() {
-      LongSummaryStatistics stats = summaryStatistics();
-      if (stats.getCount() == 0) {
-        return OptionalDouble.empty();
-      }
-      return OptionalDouble.of(stats.getAverage());
-    }
-
-    @Override
-    public LongSummaryStatistics summaryStatistics() {
-      return collect(
-          LongSummaryStatistics::new,
-          // TODO switch to a lambda reference once #9340 is fixed
-          (longSummaryStatistics, value) -> longSummaryStatistics.accept(value),
-          LongSummaryStatistics::combine);
-    }
-
-    @Override
-    public boolean anyMatch(LongPredicate predicate) {
-      return filter(predicate).findFirst().isPresent();
-    }
-
-    @Override
-    public boolean allMatch(LongPredicate predicate) {
-      return !anyMatch(predicate.negate());
-    }
-
-    @Override
-    public boolean noneMatch(LongPredicate predicate) {
-      return !anyMatch(predicate);
-    }
-
-    @Override
-    public OptionalLong findFirst() {
-      terminate();
-      ValueConsumer holder = new ValueConsumer();
-      if (spliterator.tryAdvance(holder)) {
-        return OptionalLong.of(holder.value);
-      }
-      return OptionalLong.empty();
-    }
-
-    @Override
-    public OptionalLong findAny() {
-      return findFirst();
-    }
-
-    @Override
-    public PrimitiveIterator.OfLong iterator() {
-      return Spliterators.iterator(spliterator());
-    }
-
-    @Override
-    public Spliterator.OfLong spliterator() {
-      terminate();
-      return spliterator;
-    }
-    // end terminals
-
-    // intermediates
-    @Override
-    public LongStream filter(LongPredicate predicate) {
-      throwIfTerminated();
-      return new LongStreamSource(this, new FilterSpliterator(predicate, spliterator));
-    }
-
-    @Override
-    public LongStream map(LongUnaryOperator mapper) {
-      throwIfTerminated();
-      return new LongStream.LongStreamSource(this, new MapToLongSpliterator(mapper, spliterator));
-    }
-
-    @Override
-    public <U> Stream<U> mapToObj(LongFunction<? extends U> mapper) {
-      throwIfTerminated();
-      return new Stream.StreamSource<U>(this, new MapToObjSpliterator<U>(mapper, spliterator));
-    }
-
-    @Override
-    public IntStream mapToInt(LongToIntFunction mapper) {
-      throwIfTerminated();
-      return new IntStream.IntStreamSource(this, new MapToIntSpliterator(mapper, spliterator));
-    }
-
-    @Override
-    public DoubleStream mapToDouble(LongToDoubleFunction mapper) {
-      throwIfTerminated();
-      return new DoubleStream.DoubleStreamSource(
-          this, new MapToDoubleSpliterator(mapper, spliterator));
-    }
-
-    @Override
-    public LongStream flatMap(LongFunction<? extends LongStream> mapper) {
-      throwIfTerminated();
-      final Spliterator<? extends LongStream> spliteratorOfStreams =
-          new MapToObjSpliterator<>(mapper, spliterator);
-
-      AbstractLongSpliterator flatMapSpliterator =
-          new Spliterators.AbstractLongSpliterator(Long.MAX_VALUE, 0) {
-            LongStream nextStream;
-            Spliterator.OfLong next;
-
-            @Override
-            public boolean tryAdvance(LongConsumer action) {
-              // look for a new spliterator
-              while (advanceToNextSpliterator()) {
-                // if we have one, try to read and use it
-                if (next.tryAdvance(action)) {
-                  return true;
-                } else {
-                  nextStream.close();
-                  nextStream = null;
-                  // failed, null it out so we can find another
-                  next = null;
-                }
-              }
-              return false;
-            }
-
-            private boolean advanceToNextSpliterator() {
-              while (next == null) {
-                if (!spliteratorOfStreams.tryAdvance(
-                    n -> {
-                      if (n != null) {
-                        nextStream = n;
-                        next = n.spliterator();
-                      }
-                    })) {
-                  return false;
-                }
-              }
-              return true;
-            }
-          };
-
-      return new LongStreamSource(this, flatMapSpliterator);
-    }
-
-    @Override
-    public LongStream distinct() {
-      throwIfTerminated();
-      HashSet<Long> seen = new HashSet<>();
-      return filter(seen::add);
-    }
-
-    @Override
-    public LongStream sorted() {
-      throwIfTerminated();
-
-      AbstractLongSpliterator sortedSpliterator =
-          new Spliterators.AbstractLongSpliterator(
-              spliterator.estimateSize(), spliterator.characteristics() | Spliterator.SORTED) {
-            Spliterator.OfLong ordered = null;
-
-            @Override
-            public Comparator<? super Long> getComparator() {
-              return null;
-            }
-
-            @Override
-            public boolean tryAdvance(LongConsumer action) {
-              if (ordered == null) {
-                long[] list = new long[0];
-                spliterator.forEachRemaining((long item) -> list[list.length] = item);
-                Arrays.sort(list);
-                ordered = Spliterators.spliterator(list, characteristics());
-              }
-              return ordered.tryAdvance(action);
-            }
-          };
-
-      return new LongStreamSource(this, sortedSpliterator);
-    }
-
-    @Override
-    public LongStream peek(LongConsumer action) {
-      checkNotNull(action);
-      throwIfTerminated();
-
-      AbstractLongSpliterator peekSpliterator =
-          new Spliterators.AbstractLongSpliterator(
-              spliterator.estimateSize(), spliterator.characteristics()) {
-            @Override
-            public boolean tryAdvance(final LongConsumer innerAction) {
-              return spliterator.tryAdvance(action.andThen(innerAction));
-            }
-          };
-
-      return new LongStreamSource(this, peekSpliterator);
-    }
-
-    @Override
-    public LongStream limit(long maxSize) {
-      throwIfTerminated();
-      checkState(maxSize >= 0, "maxSize may not be negative");
-      return new LongStreamSource(this, new LimitSpliterator(maxSize, spliterator));
-    }
-
-    @Override
-    public LongStream skip(long n) {
-      throwIfTerminated();
-      checkState(n >= 0, "n may not be negative");
-      if (n == 0) {
-        return this;
-      }
-      return new LongStreamSource(this, new SkipSpliterator(n, spliterator));
-    }
-
-    @Override
-    public DoubleStream asDoubleStream() {
-      return mapToDouble(x -> (double) x);
-    }
-
-    @Override
-    public Stream<Long> boxed() {
-      return mapToObj(Long::valueOf);
-    }
-
-    @Override
-    public LongStream sequential() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public LongStream parallel() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public boolean isParallel() {
-      throwIfTerminated();
-      return false;
-    }
-
-    @Override
-    public LongStream unordered() {
-      throwIfTerminated();
-      return this;
-    }
-  }
 }
diff --git a/user/super/com/google/gwt/emul/java/util/stream/LongStreamImpl.java b/user/super/com/google/gwt/emul/java/util/stream/LongStreamImpl.java
new file mode 100644
index 0000000..ee9a379
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/util/stream/LongStreamImpl.java
@@ -0,0 +1,819 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package java.util.stream;
+
+import static javaemul.internal.InternalPreconditions.checkNotNull;
+import static javaemul.internal.InternalPreconditions.checkState;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LongSummaryStatistics;
+import java.util.OptionalDouble;
+import java.util.OptionalLong;
+import java.util.PrimitiveIterator;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.Spliterators.AbstractLongSpliterator;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongBinaryOperator;
+import java.util.function.LongConsumer;
+import java.util.function.LongFunction;
+import java.util.function.LongPredicate;
+import java.util.function.LongToDoubleFunction;
+import java.util.function.LongToIntFunction;
+import java.util.function.LongUnaryOperator;
+import java.util.function.ObjLongConsumer;
+import java.util.function.Supplier;
+
+/**
+ * Main implementation of LongStream, wrapping a single spliterator, and an optional parent stream.
+ */
+final class LongStreamImpl extends TerminatableStream<LongStreamImpl> implements LongStream {
+
+  /**
+   * Represents an empty stream, doing nothing for all methods.
+   */
+  static class Empty extends TerminatableStream<Empty> implements LongStream {
+    public Empty(TerminatableStream<?> previous) {
+      super(previous);
+    }
+
+    @Override
+    public LongStream filter(LongPredicate predicate) {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public LongStream map(LongUnaryOperator mapper) {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public <U> Stream<U> mapToObj(LongFunction<? extends U> mapper) {
+      throwIfTerminated();
+      return new StreamImpl.Empty<U>(this);
+    }
+
+    @Override
+    public IntStream mapToInt(LongToIntFunction mapper) {
+      throwIfTerminated();
+      return new IntStreamImpl.Empty(this);
+    }
+
+    @Override
+    public DoubleStream mapToDouble(LongToDoubleFunction mapper) {
+      throwIfTerminated();
+      return new DoubleStreamImpl.Empty(this);
+    }
+
+    @Override
+    public LongStream flatMap(LongFunction<? extends LongStream> mapper) {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public LongStream distinct() {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public LongStream sorted() {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public LongStream peek(LongConsumer action) {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public LongStream limit(long maxSize) {
+      throwIfTerminated();
+      checkState(maxSize >= 0, "maxSize may not be negative");
+      return this;
+    }
+
+    @Override
+    public LongStream skip(long n) {
+      throwIfTerminated();
+      checkState(n >= 0, "n may not be negative");
+      return this;
+    }
+
+    @Override
+    public void forEach(LongConsumer action) {
+      terminate();
+    }
+
+    @Override
+    public void forEachOrdered(LongConsumer action) {
+      terminate();
+    }
+
+    @Override
+    public long[] toArray() {
+      terminate();
+      return new long[0];
+    }
+
+    @Override
+    public long reduce(long identity, LongBinaryOperator op) {
+      terminate();
+      return identity;
+    }
+
+    @Override
+    public OptionalLong reduce(LongBinaryOperator op) {
+      terminate();
+      return OptionalLong.empty();
+    }
+
+    @Override
+    public <R> R collect(
+        Supplier<R> supplier, ObjLongConsumer<R> accumulator, BiConsumer<R, R> combiner) {
+      terminate();
+      return supplier.get();
+    }
+
+    @Override
+    public long sum() {
+      terminate();
+      return 0;
+    }
+
+    @Override
+    public OptionalLong min() {
+      terminate();
+      return OptionalLong.empty();
+    }
+
+    @Override
+    public OptionalLong max() {
+      terminate();
+      return OptionalLong.empty();
+    }
+
+    @Override
+    public long count() {
+      terminate();
+      return 0;
+    }
+
+    @Override
+    public OptionalDouble average() {
+      terminate();
+      return OptionalDouble.empty();
+    }
+
+    @Override
+    public LongSummaryStatistics summaryStatistics() {
+      terminate();
+      return new LongSummaryStatistics();
+    }
+
+    @Override
+    public boolean anyMatch(LongPredicate predicate) {
+      terminate();
+      return false;
+    }
+
+    @Override
+    public boolean allMatch(LongPredicate predicate) {
+      terminate();
+      return true;
+    }
+
+    @Override
+    public boolean noneMatch(LongPredicate predicate) {
+      terminate();
+      return true;
+    }
+
+    @Override
+    public OptionalLong findFirst() {
+      terminate();
+      return OptionalLong.empty();
+    }
+
+    @Override
+    public OptionalLong findAny() {
+      terminate();
+      return OptionalLong.empty();
+    }
+
+    @Override
+    public DoubleStream asDoubleStream() {
+      throwIfTerminated();
+      return new DoubleStreamImpl.Empty(this);
+    }
+
+    @Override
+    public Stream<Long> boxed() {
+      throwIfTerminated();
+      return new StreamImpl.Empty<Long>(this);
+    }
+
+    @Override
+    public LongStream sequential() {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public LongStream parallel() {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public PrimitiveIterator.OfLong iterator() {
+      return Spliterators.iterator(spliterator());
+    }
+
+    @Override
+    public Spliterator.OfLong spliterator() {
+      terminate();
+      return Spliterators.emptyLongSpliterator();
+    }
+
+    @Override
+    public boolean isParallel() {
+      throwIfTerminated();
+      return false;
+    }
+
+    @Override
+    public LongStream unordered() {
+      throwIfTerminated();
+      return this;
+    }
+  }
+
+  /**
+   * Long to Int map spliterator.
+   */
+  private static final class MapToIntSpliterator extends Spliterators.AbstractIntSpliterator {
+    private final LongToIntFunction map;
+    private final Spliterator.OfLong original;
+
+    public MapToIntSpliterator(LongToIntFunction map, Spliterator.OfLong original) {
+      super(
+          original.estimateSize(),
+          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
+      checkNotNull(map);
+      this.map = map;
+      this.original = original;
+    }
+
+    @Override
+    public boolean tryAdvance(final IntConsumer action) {
+      return original.tryAdvance((long u) -> action.accept(map.applyAsInt(u)));
+    }
+  }
+
+  /**
+   * Long to Object map spliterator.
+   *
+   * @param <T> the type of data in the object spliterator
+   */
+  private static final class MapToObjSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
+    private final LongFunction<? extends T> map;
+    private final Spliterator.OfLong original;
+
+    public MapToObjSpliterator(LongFunction<? extends T> map, Spliterator.OfLong original) {
+      super(
+          original.estimateSize(),
+          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
+      checkNotNull(map);
+      this.map = map;
+      this.original = original;
+    }
+
+    @Override
+    public boolean tryAdvance(final Consumer<? super T> action) {
+      return original.tryAdvance((long u) -> action.accept(map.apply(u)));
+    }
+  }
+
+  /**
+   * Long to Long map spliterator.
+   */
+  private static final class MapToLongSpliterator extends Spliterators.AbstractLongSpliterator {
+    private final LongUnaryOperator map;
+    private final Spliterator.OfLong original;
+
+    public MapToLongSpliterator(LongUnaryOperator map, Spliterator.OfLong original) {
+      super(
+          original.estimateSize(),
+          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
+      checkNotNull(map);
+      this.map = map;
+      this.original = original;
+    }
+
+    @Override
+    public boolean tryAdvance(final LongConsumer action) {
+      return original.tryAdvance((long u) -> action.accept(map.applyAsLong(u)));
+    }
+  }
+
+  /**
+   * Long to Double map Spliterator.
+   */
+  private static final class MapToDoubleSpliterator extends Spliterators.AbstractDoubleSpliterator {
+    private final LongToDoubleFunction map;
+    private final Spliterator.OfLong original;
+
+    public MapToDoubleSpliterator(LongToDoubleFunction map, Spliterator.OfLong original) {
+      super(
+          original.estimateSize(),
+          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
+      checkNotNull(map);
+      this.map = map;
+      this.original = original;
+    }
+
+    @Override
+    public boolean tryAdvance(final DoubleConsumer action) {
+      return original.tryAdvance((long u) -> action.accept(map.applyAsDouble(u)));
+    }
+  }
+
+  /**
+   * Long filter spliterator.
+   */
+  private static final class FilterSpliterator extends Spliterators.AbstractLongSpliterator {
+    private final LongPredicate filter;
+    private final Spliterator.OfLong original;
+
+    private boolean found;
+
+    public FilterSpliterator(LongPredicate filter, Spliterator.OfLong original) {
+      super(original.estimateSize(), original.characteristics() & ~Spliterator.SIZED);
+      checkNotNull(filter);
+      this.filter = filter;
+      this.original = original;
+    }
+
+    @Override
+    public Comparator<? super Long> getComparator() {
+      return original.getComparator();
+    }
+
+    @Override
+    public boolean tryAdvance(final LongConsumer action) {
+      found = false;
+      while (!found
+          && original.tryAdvance(
+              (long item) -> {
+                if (filter.test(item)) {
+                  found = true;
+                  action.accept(item);
+                }
+              })) {
+        // do nothing, work is done in tryAdvance
+      }
+
+      return found;
+    }
+  }
+
+  /**
+   * Long skip spliterator.
+   */
+  private static final class SkipSpliterator extends Spliterators.AbstractLongSpliterator {
+    private long skip;
+    private final Spliterator.OfLong original;
+
+    public SkipSpliterator(long skip, Spliterator.OfLong original) {
+      super(
+          original.hasCharacteristics(Spliterator.SIZED)
+              ? Math.max(0, original.estimateSize() - skip)
+              : Long.MAX_VALUE,
+          original.characteristics());
+      this.skip = skip;
+      this.original = original;
+    }
+
+    @Override
+    public Comparator<? super Long> getComparator() {
+      return original.getComparator();
+    }
+
+    @Override
+    public boolean tryAdvance(LongConsumer action) {
+      while (skip > 0) {
+        if (!original.tryAdvance((long ignore) -> { })) {
+          return false;
+        }
+        skip--;
+      }
+      return original.tryAdvance(action);
+    }
+  }
+
+  /**
+   * Long limit spliterator.
+   */
+  private static final class LimitSpliterator extends Spliterators.AbstractLongSpliterator {
+    private final long limit;
+    private final Spliterator.OfLong original;
+    private int position = 0;
+
+    public LimitSpliterator(long limit, Spliterator.OfLong original) {
+      super(
+          original.hasCharacteristics(Spliterator.SIZED)
+              ? Math.min(original.estimateSize(), limit)
+              : Long.MAX_VALUE,
+          original.characteristics());
+      this.limit = limit;
+      this.original = original;
+    }
+
+    @Override
+    public Comparator<? super Long> getComparator() {
+      return original.getComparator();
+    }
+
+    @Override
+    public boolean tryAdvance(LongConsumer action) {
+      if (position >= limit) {
+        return false;
+      }
+      boolean result = original.tryAdvance(action);
+      position++;
+      return result;
+    }
+  }
+
+  /**
+   * Value holder for various stream operations.
+   */
+  private static final class ValueConsumer implements LongConsumer {
+    long value;
+
+    @Override
+    public void accept(long value) {
+      this.value = value;
+    }
+  }
+
+  private final Spliterator.OfLong spliterator;
+
+  public LongStreamImpl(TerminatableStream<?> previous, Spliterator.OfLong spliterator) {
+    super(previous);
+    this.spliterator = spliterator;
+  }
+
+  // terminals
+
+  @Override
+  public void forEach(LongConsumer action) {
+    forEachOrdered(action);
+  }
+
+  @Override
+  public void forEachOrdered(LongConsumer action) {
+    terminate();
+    spliterator.forEachRemaining(action);
+  }
+
+  @Override
+  public long[] toArray() {
+    terminate();
+    long[] entries = new long[0];
+    // this is legal in js, since the array will be backed by a JS array
+    spliterator.forEachRemaining((long value) -> entries[entries.length] = value);
+
+    return entries;
+  }
+
+  @Override
+  public long reduce(long identity, LongBinaryOperator op) {
+    terminate();
+    ValueConsumer holder = new ValueConsumer();
+    holder.value = identity;
+    spliterator.forEachRemaining(
+        (long value) -> {
+          holder.accept(op.applyAsLong(holder.value, value));
+        });
+    return holder.value;
+  }
+
+  @Override
+  public OptionalLong reduce(LongBinaryOperator op) {
+    ValueConsumer holder = new ValueConsumer();
+    if (spliterator.tryAdvance(holder)) {
+      return OptionalLong.of(reduce(holder.value, op));
+    }
+    terminate();
+    return OptionalLong.empty();
+  }
+
+  @Override
+  public <R> R collect(
+      Supplier<R> supplier, ObjLongConsumer<R> accumulator, BiConsumer<R, R> combiner) {
+    terminate();
+    final R acc = supplier.get();
+    spliterator.forEachRemaining((long value) -> accumulator.accept(acc, value));
+    return acc;
+  }
+
+  @Override
+  public long sum() {
+    return summaryStatistics().getSum();
+  }
+
+  @Override
+  public OptionalLong min() {
+    LongSummaryStatistics stats = summaryStatistics();
+    if (stats.getCount() == 0) {
+      return OptionalLong.empty();
+    }
+    return OptionalLong.of(stats.getMin());
+  }
+
+  @Override
+  public OptionalLong max() {
+    LongSummaryStatistics stats = summaryStatistics();
+    if (stats.getCount() == 0) {
+      return OptionalLong.empty();
+    }
+    return OptionalLong.of(stats.getMax());
+  }
+
+  @Override
+  public long count() {
+    terminate();
+    long count = 0;
+    while (spliterator.tryAdvance((long value) -> { })) {
+      count++;
+    }
+    return count;
+  }
+
+  @Override
+  public OptionalDouble average() {
+    LongSummaryStatistics stats = summaryStatistics();
+    if (stats.getCount() == 0) {
+      return OptionalDouble.empty();
+    }
+    return OptionalDouble.of(stats.getAverage());
+  }
+
+  @Override
+  public LongSummaryStatistics summaryStatistics() {
+    return collect(
+        LongSummaryStatistics::new,
+        // TODO switch to a lambda reference once #9340 is fixed
+        (longSummaryStatistics, value) -> longSummaryStatistics.accept(value),
+        LongSummaryStatistics::combine);
+  }
+
+  @Override
+  public boolean anyMatch(LongPredicate predicate) {
+    return filter(predicate).findFirst().isPresent();
+  }
+
+  @Override
+  public boolean allMatch(LongPredicate predicate) {
+    return !anyMatch(predicate.negate());
+  }
+
+  @Override
+  public boolean noneMatch(LongPredicate predicate) {
+    return !anyMatch(predicate);
+  }
+
+  @Override
+  public OptionalLong findFirst() {
+    terminate();
+    ValueConsumer holder = new ValueConsumer();
+    if (spliterator.tryAdvance(holder)) {
+      return OptionalLong.of(holder.value);
+    }
+    return OptionalLong.empty();
+  }
+
+  @Override
+  public OptionalLong findAny() {
+    return findFirst();
+  }
+
+  @Override
+  public PrimitiveIterator.OfLong iterator() {
+    return Spliterators.iterator(spliterator());
+  }
+
+  @Override
+  public Spliterator.OfLong spliterator() {
+    terminate();
+    return spliterator;
+  }
+  // end terminals
+
+  // intermediates
+  @Override
+  public LongStream filter(LongPredicate predicate) {
+    throwIfTerminated();
+    return new LongStreamImpl(this, new FilterSpliterator(predicate, spliterator));
+  }
+
+  @Override
+  public LongStream map(LongUnaryOperator mapper) {
+    throwIfTerminated();
+    return new LongStreamImpl(this, new MapToLongSpliterator(mapper, spliterator));
+  }
+
+  @Override
+  public <U> Stream<U> mapToObj(LongFunction<? extends U> mapper) {
+    throwIfTerminated();
+    return new StreamImpl<U>(this, new MapToObjSpliterator<U>(mapper, spliterator));
+  }
+
+  @Override
+  public IntStream mapToInt(LongToIntFunction mapper) {
+    throwIfTerminated();
+    return new IntStreamImpl(this, new MapToIntSpliterator(mapper, spliterator));
+  }
+
+  @Override
+  public DoubleStream mapToDouble(LongToDoubleFunction mapper) {
+    throwIfTerminated();
+    return new DoubleStreamImpl(this, new MapToDoubleSpliterator(mapper, spliterator));
+  }
+
+  @Override
+  public LongStream flatMap(LongFunction<? extends LongStream> mapper) {
+    throwIfTerminated();
+    final Spliterator<? extends LongStream> spliteratorOfStreams =
+        new MapToObjSpliterator<>(mapper, spliterator);
+
+    AbstractLongSpliterator flatMapSpliterator =
+        new Spliterators.AbstractLongSpliterator(Long.MAX_VALUE, 0) {
+          LongStream nextStream;
+          Spliterator.OfLong next;
+
+          @Override
+          public boolean tryAdvance(LongConsumer action) {
+            // look for a new spliterator
+            while (advanceToNextSpliterator()) {
+              // if we have one, try to read and use it
+              if (next.tryAdvance(action)) {
+                return true;
+              } else {
+                nextStream.close();
+                nextStream = null;
+                // failed, null it out so we can find another
+                next = null;
+              }
+            }
+            return false;
+          }
+
+          private boolean advanceToNextSpliterator() {
+            while (next == null) {
+              if (!spliteratorOfStreams.tryAdvance(
+                  n -> {
+                    if (n != null) {
+                      nextStream = n;
+                      next = n.spliterator();
+                    }
+                  })) {
+                return false;
+              }
+            }
+            return true;
+          }
+        };
+
+    return new LongStreamImpl(this, flatMapSpliterator);
+  }
+
+  @Override
+  public LongStream distinct() {
+    throwIfTerminated();
+    HashSet<Long> seen = new HashSet<>();
+    return filter(seen::add);
+  }
+
+  @Override
+  public LongStream sorted() {
+    throwIfTerminated();
+
+    AbstractLongSpliterator sortedSpliterator =
+        new Spliterators.AbstractLongSpliterator(
+            spliterator.estimateSize(), spliterator.characteristics() | Spliterator.SORTED) {
+          Spliterator.OfLong ordered = null;
+
+          @Override
+          public Comparator<? super Long> getComparator() {
+            return null;
+          }
+
+          @Override
+          public boolean tryAdvance(LongConsumer action) {
+            if (ordered == null) {
+              long[] list = new long[0];
+              spliterator.forEachRemaining((long item) -> list[list.length] = item);
+              Arrays.sort(list);
+              ordered = Spliterators.spliterator(list, characteristics());
+            }
+            return ordered.tryAdvance(action);
+          }
+        };
+
+    return new LongStreamImpl(this, sortedSpliterator);
+  }
+
+  @Override
+  public LongStream peek(LongConsumer action) {
+    checkNotNull(action);
+    throwIfTerminated();
+
+    AbstractLongSpliterator peekSpliterator =
+        new Spliterators.AbstractLongSpliterator(
+            spliterator.estimateSize(), spliterator.characteristics()) {
+          @Override
+          public boolean tryAdvance(final LongConsumer innerAction) {
+            return spliterator.tryAdvance(action.andThen(innerAction));
+          }
+        };
+
+    return new LongStreamImpl(this, peekSpliterator);
+  }
+
+  @Override
+  public LongStream limit(long maxSize) {
+    throwIfTerminated();
+    checkState(maxSize >= 0, "maxSize may not be negative");
+    return new LongStreamImpl(this, new LimitSpliterator(maxSize, spliterator));
+  }
+
+  @Override
+  public LongStream skip(long n) {
+    throwIfTerminated();
+    checkState(n >= 0, "n may not be negative");
+    if (n == 0) {
+      return this;
+    }
+    return new LongStreamImpl(this, new SkipSpliterator(n, spliterator));
+  }
+
+  @Override
+  public DoubleStream asDoubleStream() {
+    return mapToDouble(x -> (double) x);
+  }
+
+  @Override
+  public Stream<Long> boxed() {
+    return mapToObj(Long::valueOf);
+  }
+
+  @Override
+  public LongStream sequential() {
+    throwIfTerminated();
+    return this;
+  }
+
+  @Override
+  public LongStream parallel() {
+    throwIfTerminated();
+    return this;
+  }
+
+  @Override
+  public boolean isParallel() {
+    throwIfTerminated();
+    return false;
+  }
+
+  @Override
+  public LongStream unordered() {
+    throwIfTerminated();
+    return this;
+  }
+}
diff --git a/user/super/com/google/gwt/emul/java/util/stream/Stream.java b/user/super/com/google/gwt/emul/java/util/stream/Stream.java
index a313f4a..a963279 100644
--- a/user/super/com/google/gwt/emul/java/util/stream/Stream.java
+++ b/user/super/com/google/gwt/emul/java/util/stream/Stream.java
@@ -13,35 +13,23 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package java.util.stream;
 
-import static javaemul.internal.InternalPreconditions.checkNotNull;
 import static javaemul.internal.InternalPreconditions.checkState;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
 import java.util.Optional;
 import java.util.Spliterator;
 import java.util.Spliterators;
-import java.util.Spliterators.AbstractDoubleSpliterator;
-import java.util.Spliterators.AbstractIntSpliterator;
-import java.util.Spliterators.AbstractLongSpliterator;
 import java.util.Spliterators.AbstractSpliterator;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.util.function.BinaryOperator;
 import java.util.function.Consumer;
-import java.util.function.DoubleConsumer;
 import java.util.function.Function;
-import java.util.function.IntConsumer;
 import java.util.function.IntFunction;
-import java.util.function.LongConsumer;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 import java.util.function.ToDoubleFunction;
@@ -56,16 +44,21 @@
  * @param <T> the type of data being streamed
  */
 public interface Stream<T> extends BaseStream<T, Stream<T>> {
-  /**
-   * Value holder for various stream operations.
-   */
-  static final class ValueConsumer<T> implements Consumer<T> {
-    T value;
 
+  /**
+   * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.Builder.html">
+   * the official Java API doc</a> for details.
+   */
+  interface Builder<T> extends Consumer<T> {
     @Override
-    public void accept(T value) {
-      this.value = value;
+    void accept(T t);
+
+    default Stream.Builder<T> add(T t) {
+      accept(t);
+      return this;
     }
+
+    Stream<T> build();
   }
 
   static <T> Stream.Builder<T> builder() {
@@ -134,7 +127,7 @@
           }
         };
 
-    Stream<T> result = new StreamSource<T>(null, spliterator);
+    Stream<T> result = new StreamImpl<T>(null, spliterator);
 
     result.onClose(a::close);
     result.onClose(b::close);
@@ -143,7 +136,7 @@
   }
 
   static <T> Stream<T> empty() {
-    return new EmptyStreamSource<T>(null);
+    return new StreamImpl.Empty<T>(null);
   }
 
   static <T> Stream<T> generate(Supplier<T> s) {
@@ -185,22 +178,6 @@
     return Arrays.stream(values);
   }
 
-  /**
-   * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.Builder.html">
-   * the official Java API doc</a> for details.
-   */
-  public interface Builder<T> extends Consumer<T> {
-    @Override
-    void accept(T t);
-
-    default Stream.Builder<T> add(T t) {
-      accept(t);
-      return this;
-    }
-
-    Stream<T> build();
-  }
-
   boolean allMatch(Predicate<? super T> predicate);
 
   boolean anyMatch(Predicate<? super T> predicate);
@@ -265,930 +242,4 @@
   Object[] toArray();
 
   <A> A[] toArray(IntFunction<A[]> generator);
-
-  /**
-   * Represents an empty stream, doing nothing for all methods.
-   */
-  static class EmptyStreamSource<T> extends TerminatableStream<EmptyStreamSource<T>>
-      implements Stream<T> {
-
-    public EmptyStreamSource(TerminatableStream<?> previous) {
-      super(previous);
-    }
-
-    @Override
-    public Stream<T> filter(Predicate<? super T> predicate) {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public <R> Stream<R> map(Function<? super T, ? extends R> mapper) {
-      throwIfTerminated();
-      return (Stream) this;
-    }
-
-    @Override
-    public IntStream mapToInt(ToIntFunction<? super T> mapper) {
-      throwIfTerminated();
-      return new IntStream.EmptyIntStreamSource(this);
-    }
-
-    @Override
-    public LongStream mapToLong(ToLongFunction<? super T> mapper) {
-      throwIfTerminated();
-      return new LongStream.EmptyLongStreamSource(this);
-    }
-
-    @Override
-    public DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper) {
-      throwIfTerminated();
-      return new DoubleStream.EmptyDoubleStreamSource(this);
-    }
-
-    @Override
-    public <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) {
-      throwIfTerminated();
-      return (Stream) this;
-    }
-
-    @Override
-    public IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) {
-      throwIfTerminated();
-      return new IntStream.EmptyIntStreamSource(this);
-    }
-
-    @Override
-    public LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper) {
-      throwIfTerminated();
-      return new LongStream.EmptyLongStreamSource(this);
-    }
-
-    @Override
-    public DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper) {
-      throwIfTerminated();
-      return new DoubleStream.EmptyDoubleStreamSource(this);
-    }
-
-    @Override
-    public Stream<T> distinct() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public Stream<T> sorted() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public Stream<T> sorted(Comparator<? super T> comparator) {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public Stream<T> peek(Consumer<? super T> action) {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public Stream<T> limit(long maxSize) {
-      throwIfTerminated();
-      checkState(maxSize >= 0, "maxSize may not be negative");
-      return this;
-    }
-
-    @Override
-    public Stream<T> skip(long n) {
-      throwIfTerminated();
-      checkState(n >= 0, "n may not be negative");
-      return this;
-    }
-
-    @Override
-    public void forEach(Consumer<? super T> action) {
-      terminate();
-      // nothing to do
-    }
-
-    @Override
-    public void forEachOrdered(Consumer<? super T> action) {
-      terminate();
-      // nothing to do
-    }
-
-    @Override
-    public Object[] toArray() {
-      terminate();
-      return new Object[0];
-    }
-
-    @Override
-    public <A> A[] toArray(IntFunction<A[]> generator) {
-      terminate();
-      return generator.apply(0);
-    }
-
-    @Override
-    public T reduce(T identity, BinaryOperator<T> accumulator) {
-      terminate();
-      return identity;
-    }
-
-    @Override
-    public Optional<T> reduce(BinaryOperator<T> accumulator) {
-      terminate();
-      return Optional.empty();
-    }
-
-    @Override
-    public <U> U reduce(
-        U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner) {
-      terminate();
-      return identity;
-    }
-
-    @Override
-    public <R> R collect(
-        Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner) {
-      terminate();
-      return supplier.get();
-    }
-
-    @Override
-    public <R, A> R collect(Collector<? super T, A, R> collector) {
-      terminate();
-      return collector.finisher().apply(collector.supplier().get());
-    }
-
-    @Override
-    public Optional<T> min(Comparator<? super T> comparator) {
-      terminate();
-      return Optional.empty();
-    }
-
-    @Override
-    public Optional<T> max(Comparator<? super T> comparator) {
-      terminate();
-      return Optional.empty();
-    }
-
-    @Override
-    public long count() {
-      terminate();
-      return 0;
-    }
-
-    @Override
-    public boolean anyMatch(Predicate<? super T> predicate) {
-      terminate();
-      return false;
-    }
-
-    @Override
-    public boolean allMatch(Predicate<? super T> predicate) {
-      terminate();
-      return true;
-    }
-
-    @Override
-    public boolean noneMatch(Predicate<? super T> predicate) {
-      terminate();
-      return true;
-    }
-
-    @Override
-    public Optional<T> findFirst() {
-      terminate();
-      return Optional.empty();
-    }
-
-    @Override
-    public Optional<T> findAny() {
-      terminate();
-      return Optional.empty();
-    }
-
-    @Override
-    public Iterator<T> iterator() {
-      terminate();
-      return Collections.emptyIterator();
-    }
-
-    @Override
-    public Spliterator<T> spliterator() {
-      terminate();
-      return Spliterators.emptySpliterator();
-    }
-
-    @Override
-    public boolean isParallel() {
-      throwIfTerminated();
-      return false;
-    }
-
-    @Override
-    public Stream<T> sequential() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public Stream<T> parallel() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public Stream<T> unordered() {
-      throwIfTerminated();
-      return this;
-    }
-  }
-
-  /**
-   * Object to Object map spliterator.
-   * @param <U> the input type
-   * @param <T> the output type
-   */
-  static final class MapToObjSpliterator<U, T> extends Spliterators.AbstractSpliterator<T> {
-    private final Function<? super U, ? extends T> map;
-    private final Spliterator<U> original;
-
-    public MapToObjSpliterator(Function<? super U, ? extends T> map, Spliterator<U> original) {
-      super(
-          original.estimateSize(),
-          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
-      checkNotNull(map);
-      this.map = map;
-      this.original = original;
-    }
-
-    @Override
-    public boolean tryAdvance(final Consumer<? super T> action) {
-      return original.tryAdvance(u -> action.accept(map.apply(u)));
-    }
-  }
-
-  /**
-   * Object to Int map spliterator.
-   * @param <T> the input type
-   */
-  static final class MapToIntSpliterator<T> extends Spliterators.AbstractIntSpliterator {
-    private final ToIntFunction<? super T> map;
-    private final Spliterator<T> original;
-
-    public MapToIntSpliterator(ToIntFunction<? super T> map, Spliterator<T> original) {
-      super(
-          original.estimateSize(),
-          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
-      checkNotNull(map);
-      this.map = map;
-      this.original = original;
-    }
-
-    @Override
-    public boolean tryAdvance(final IntConsumer action) {
-      return original.tryAdvance(u -> action.accept(map.applyAsInt(u)));
-    }
-  }
-
-  /**
-   * Object to Long map spliterator.
-   * @param <T> the input type
-   */
-  static final class MapToLongSpliterator<T> extends Spliterators.AbstractLongSpliterator {
-    private final ToLongFunction<? super T> map;
-    private final Spliterator<T> original;
-
-    public MapToLongSpliterator(ToLongFunction<? super T> map, Spliterator<T> original) {
-      super(
-          original.estimateSize(),
-          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
-      checkNotNull(map);
-      this.map = map;
-      this.original = original;
-    }
-
-    @Override
-    public boolean tryAdvance(final LongConsumer action) {
-      return original.tryAdvance(u -> action.accept(map.applyAsLong(u)));
-    }
-  }
-
-  /**
-   * Object to Double map spliterator.
-   * @param <T> the input type
-   */
-  static final class MapToDoubleSpliterator<T> extends Spliterators.AbstractDoubleSpliterator {
-    private final ToDoubleFunction<? super T> map;
-    private final Spliterator<T> original;
-
-    public MapToDoubleSpliterator(ToDoubleFunction<? super T> map, Spliterator<T> original) {
-      super(
-          original.estimateSize(),
-          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
-      checkNotNull(map);
-      this.map = map;
-      this.original = original;
-    }
-
-    @Override
-    public boolean tryAdvance(final DoubleConsumer action) {
-      return original.tryAdvance(u -> action.accept(map.applyAsDouble(u)));
-    }
-  }
-
-  /**
-   * Object filter spliterator.
-   * @param <T> the type of data to iterate over
-   */
-  static final class FilterSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
-    private final Predicate<? super T> filter;
-    private final Spliterator<T> original;
-
-    private boolean found;
-
-    public FilterSpliterator(Predicate<? super T> filter, Spliterator<T> original) {
-      super(original.estimateSize(), original.characteristics() & ~Spliterator.SIZED);
-      checkNotNull(filter);
-      this.filter = filter;
-      this.original = original;
-    }
-
-    @Override
-    public Comparator<? super T> getComparator() {
-      return original.getComparator();
-    }
-
-    @Override
-    public boolean tryAdvance(final Consumer<? super T> action) {
-      found = false;
-      while (!found
-          && original.tryAdvance(
-              item -> {
-                if (filter.test(item)) {
-                  found = true;
-                  action.accept(item);
-                }
-              })) {
-        // do nothing, work is done in tryAdvance
-      }
-
-      return found;
-    }
-  }
-
-  /**
-   * Object skip spliterator.
-   * @param <T> the type of data to iterate over
-   */
-  static final class SkipSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
-    private long skip;
-    private final Spliterator<T> original;
-
-    public SkipSpliterator(long skip, Spliterator<T> original) {
-      super(
-          original.hasCharacteristics(Spliterator.SIZED)
-              ? Math.max(0, original.estimateSize() - skip)
-              : Long.MAX_VALUE,
-          original.characteristics());
-      this.skip = skip;
-      this.original = original;
-    }
-
-    @Override
-    public Comparator<? super T> getComparator() {
-      return original.getComparator();
-    }
-
-    @Override
-    public boolean tryAdvance(Consumer<? super T> action) {
-      while (skip > 0) {
-        if (!original.tryAdvance(ignore -> { })) {
-          return false;
-        }
-        skip--;
-      }
-      return original.tryAdvance(action);
-    }
-  }
-
-  /**
-   * Object limit spliterator.
-   * @param <T> the type of data to iterate over
-   */
-  static final class LimitSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
-    private final long limit;
-    private final Spliterator<T> original;
-    private int position = 0;
-
-    public LimitSpliterator(long limit, Spliterator<T> original) {
-      super(
-          original.hasCharacteristics(Spliterator.SIZED)
-              ? Math.min(original.estimateSize(), limit)
-              : Long.MAX_VALUE,
-          original.characteristics());
-      this.limit = limit;
-      this.original = original;
-    }
-
-    @Override
-    public Comparator<? super T> getComparator() {
-      return original.getComparator();
-    }
-
-    @Override
-    public boolean tryAdvance(Consumer<? super T> action) {
-      if (position >= limit) {
-        return false;
-      }
-      boolean result = original.tryAdvance(action);
-      position++;
-      return result;
-    }
-  }
-
-  /**
-   * Main implementation of Stream, wrapping a single spliterator and an optional parent stream.
-   * @param <T>
-   */
-  static class StreamSource<T> extends TerminatableStream<StreamSource<T>> implements Stream<T> {
-    private final Spliterator<T> spliterator;
-
-    public StreamSource(TerminatableStream<?> prev, Spliterator<T> spliterator) {
-      super(prev);
-      this.spliterator = spliterator;
-    }
-
-    // terminal
-    @Override
-    public Spliterator<T> spliterator() {
-      terminate();
-      return spliterator;
-    }
-
-    @Override
-    public Iterator<T> iterator() {
-      return Spliterators.iterator(spliterator());
-    }
-
-    @Override
-    public long count() {
-      terminate();
-      long count = 0;
-      while (spliterator.tryAdvance(a -> { })) {
-        count++;
-      }
-      return count;
-    }
-
-    @Override
-    public void forEach(Consumer<? super T> action) {
-      forEachOrdered(action);
-    }
-
-    @Override
-    public void forEachOrdered(Consumer<? super T> action) {
-      terminate();
-      spliterator.forEachRemaining(action);
-    }
-
-    @Override
-    public Object[] toArray() {
-      return toArray(Object[]::new);
-    }
-
-    @Override
-    public <A> A[] toArray(IntFunction<A[]> generator) {
-      List<T> collected = collect(Collectors.toList());
-      return collected.toArray(generator.apply(collected.size()));
-    }
-
-    @Override
-    public <R> R collect(
-        Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner) {
-      return collect(
-          Collector.of(
-              supplier,
-              accumulator,
-              (a, b) -> {
-                combiner.accept(a, b);
-                return a;
-              }));
-    }
-
-    @Override
-    public <R, A> R collect(final Collector<? super T, A, R> collector) {
-      return collector
-          .finisher()
-          .apply(
-              reduce(
-                  collector.supplier().get(),
-                  (a, t) -> {
-                    collector.accumulator().accept(a, t);
-                    return a;
-                  },
-                  collector.combiner()));
-    }
-
-    @Override
-    public Optional<T> findFirst() {
-      terminate();
-      ValueConsumer<T> holder = new ValueConsumer<T>();
-      if (spliterator.tryAdvance(holder)) {
-        return Optional.of(holder.value);
-      }
-      return Optional.empty();
-    }
-
-    @Override
-    public Optional<T> findAny() {
-      return findFirst();
-    }
-
-    @Override
-    public boolean anyMatch(Predicate<? super T> predicate) {
-      return filter(predicate).findFirst().isPresent();
-    }
-
-    @Override
-    public boolean allMatch(final Predicate<? super T> predicate) {
-      return !anyMatch(predicate.negate());
-    }
-
-    @Override
-    public boolean noneMatch(final Predicate<? super T> predicate) {
-      return !anyMatch(predicate);
-    }
-
-    @Override
-    public Optional<T> min(final Comparator<? super T> comparator) {
-      return reduce(BinaryOperator.minBy(comparator));
-    }
-
-    @Override
-    public Optional<T> max(final Comparator<? super T> comparator) {
-      return reduce(BinaryOperator.maxBy(comparator));
-    }
-
-    @Override
-    public T reduce(T identity, BinaryOperator<T> accumulator) {
-      return reduce(identity, accumulator, accumulator);
-    }
-
-    @Override
-    public Optional<T> reduce(BinaryOperator<T> accumulator) {
-      ValueConsumer<T> consumer = new ValueConsumer<T>();
-      if (!spliterator.tryAdvance(consumer)) {
-        terminate();
-        return Optional.empty();
-      }
-      return Optional.of(reduce(consumer.value, accumulator));
-    }
-
-    // combiner is ignored, since we don't parallelize
-    @Override
-    public <U> U reduce(
-        U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner) {
-      terminate();
-      final ValueConsumer<U> consumer = new ValueConsumer<U>();
-      consumer.value = identity;
-      spliterator.forEachRemaining(
-          item -> {
-            consumer.accept(accumulator.apply(consumer.value, item));
-          });
-      return consumer.value;
-    }
-    // end terminal
-
-    // intermediate
-    @Override
-    public Stream<T> filter(Predicate<? super T> predicate) {
-      throwIfTerminated();
-      return new StreamSource<>(this, new FilterSpliterator<>(predicate, spliterator));
-    }
-
-    @Override
-    public <R> Stream<R> map(Function<? super T, ? extends R> mapper) {
-      throwIfTerminated();
-      return new StreamSource<>(this, new MapToObjSpliterator<>(mapper, spliterator));
-    }
-
-    @Override
-    public IntStream mapToInt(ToIntFunction<? super T> mapper) {
-      throwIfTerminated();
-      return new IntStream.IntStreamSource(this, new MapToIntSpliterator<>(mapper, spliterator));
-    }
-
-    @Override
-    public LongStream mapToLong(ToLongFunction<? super T> mapper) {
-      throwIfTerminated();
-      return new LongStream.LongStreamSource(this, new MapToLongSpliterator<>(mapper, spliterator));
-    }
-
-    @Override
-    public DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper) {
-      throwIfTerminated();
-      return new DoubleStream.DoubleStreamSource(
-          this, new MapToDoubleSpliterator<>(mapper, spliterator));
-    }
-
-    @Override
-    public <R> Stream<R> flatMap(final Function<? super T, ? extends Stream<? extends R>> mapper) {
-      throwIfTerminated();
-      final Spliterator<? extends Stream<? extends R>> spliteratorOfStreams =
-          new MapToObjSpliterator<>(mapper, spliterator);
-
-      AbstractSpliterator<R> flatMapSpliterator =
-          new Spliterators.AbstractSpliterator<R>(Long.MAX_VALUE, 0) {
-            Stream<? extends R> nextStream;
-            Spliterator<? extends R> next;
-
-            @Override
-            public boolean tryAdvance(Consumer<? super R> action) {
-              // look for a new spliterator
-              while (advanceToNextSpliterator()) {
-                // if we have one, try to read and use it
-                if (next.tryAdvance(action)) {
-                  return true;
-                } else {
-                  nextStream.close();
-                  nextStream = null;
-                  // failed, null it out so we can find another
-                  next = null;
-                }
-              }
-              return false;
-            }
-
-            private boolean advanceToNextSpliterator() {
-              while (next == null) {
-                if (!spliteratorOfStreams.tryAdvance(
-                    n -> {
-                      if (n != null) {
-                        nextStream = n;
-                        next = n.spliterator();
-                      }
-                    })) {
-                  return false;
-                }
-              }
-              return true;
-            }
-          };
-
-      return new StreamSource<R>(this, flatMapSpliterator);
-    }
-
-    @Override
-    public IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) {
-      throwIfTerminated();
-      final Spliterator<? extends IntStream> spliteratorOfStreams =
-          new MapToObjSpliterator<>(mapper, spliterator);
-
-      AbstractIntSpliterator flatMapSpliterator =
-          new Spliterators.AbstractIntSpliterator(Long.MAX_VALUE, 0) {
-            IntStream nextStream;
-            Spliterator.OfInt next;
-
-            @Override
-            public boolean tryAdvance(IntConsumer action) {
-              // look for a new spliterator
-              while (advanceToNextSpliterator()) {
-                // if we have one, try to read and use it
-                if (next.tryAdvance(action)) {
-                  return true;
-                } else {
-                  nextStream.close();
-                  nextStream = null;
-                  // failed, null it out so we can find another
-                  next = null;
-                }
-              }
-              return false;
-            }
-
-            private boolean advanceToNextSpliterator() {
-              while (next == null) {
-                if (!spliteratorOfStreams.tryAdvance(
-                    n -> {
-                      if (n != null) {
-                        nextStream = n;
-                        next = n.spliterator();
-                      }
-                    })) {
-                  return false;
-                }
-              }
-              return true;
-            }
-          };
-
-      return new IntStream.IntStreamSource(this, flatMapSpliterator);
-    }
-
-    @Override
-    public LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper) {
-      throwIfTerminated();
-      final Spliterator<? extends LongStream> spliteratorOfStreams =
-          new MapToObjSpliterator<>(mapper, spliterator);
-
-      AbstractLongSpliterator flatMapSpliterator =
-          new Spliterators.AbstractLongSpliterator(Long.MAX_VALUE, 0) {
-            LongStream nextStream;
-            Spliterator.OfLong next;
-
-            @Override
-            public boolean tryAdvance(LongConsumer action) {
-              // look for a new spliterator
-              while (advanceToNextSpliterator()) {
-                // if we have one, try to read and use it
-                if (next.tryAdvance(action)) {
-                  return true;
-                } else {
-                  nextStream.close();
-                  nextStream = null;
-                  // failed, null it out so we can find another
-                  next = null;
-                }
-              }
-              return false;
-            }
-
-            private boolean advanceToNextSpliterator() {
-              while (next == null) {
-                if (!spliteratorOfStreams.tryAdvance(
-                    n -> {
-                      if (n != null) {
-                        nextStream = n;
-                        next = n.spliterator();
-                      }
-                    })) {
-                  return false;
-                }
-              }
-              return true;
-            }
-          };
-
-      return new LongStream.LongStreamSource(this, flatMapSpliterator);
-    }
-
-    @Override
-    public DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper) {
-      throwIfTerminated();
-      final Spliterator<? extends DoubleStream> spliteratorOfStreams =
-          new MapToObjSpliterator<>(mapper, spliterator);
-
-      AbstractDoubleSpliterator flatMapSpliterator =
-          new Spliterators.AbstractDoubleSpliterator(Long.MAX_VALUE, 0) {
-            DoubleStream nextStream;
-            Spliterator.OfDouble next;
-
-            @Override
-            public boolean tryAdvance(DoubleConsumer action) {
-              // look for a new spliterator
-              while (advanceToNextSpliterator()) {
-                // if we have one, try to read and use it
-                if (next.tryAdvance(action)) {
-                  return true;
-                } else {
-                  nextStream.close();
-                  nextStream = null;
-                  // failed, null it out so we can find another
-                  next = null;
-                }
-              }
-              return false;
-            }
-
-            private boolean advanceToNextSpliterator() {
-              while (next == null) {
-                if (!spliteratorOfStreams.tryAdvance(
-                    n -> {
-                      if (n != null) {
-                        nextStream = n;
-                        next = n.spliterator();
-                      }
-                    })) {
-                  return false;
-                }
-              }
-              return true;
-            }
-          };
-
-      return new DoubleStream.DoubleStreamSource(this, flatMapSpliterator);
-    }
-
-    @Override
-    public Stream<T> distinct() {
-      throwIfTerminated();
-      HashSet<T> seen = new HashSet<>();
-      return filter(seen::add);
-    }
-
-    @Override
-    public Stream<T> sorted() {
-      throwIfTerminated();
-      Comparator<T> c = (Comparator) Comparator.naturalOrder();
-      return sorted(c);
-    }
-
-    @Override
-    public Stream<T> sorted(final Comparator<? super T> comparator) {
-      throwIfTerminated();
-
-      AbstractSpliterator<T> sortedSpliterator =
-          new Spliterators.AbstractSpliterator<T>(
-              spliterator.estimateSize(), spliterator.characteristics() | Spliterator.SORTED) {
-            Spliterator<T> ordered = null;
-
-            @Override
-            public Comparator<? super T> getComparator() {
-              return comparator == Comparator.naturalOrder() ? null : comparator;
-            }
-
-            @Override
-            public boolean tryAdvance(Consumer<? super T> action) {
-              if (ordered == null) {
-                List<T> list = new ArrayList<>();
-                spliterator.forEachRemaining(list::add);
-                Collections.sort(list, comparator);
-                ordered = list.spliterator();
-              }
-              return ordered.tryAdvance(action);
-            }
-          };
-
-      return new StreamSource<>(this, sortedSpliterator);
-    }
-
-    @Override
-    public Stream<T> peek(final Consumer<? super T> action) {
-      checkNotNull(action);
-      throwIfTerminated();
-
-      AbstractSpliterator<T> peekSpliterator =
-          new Spliterators.AbstractSpliterator<T>(
-              spliterator.estimateSize(), spliterator.characteristics()) {
-            @Override
-            public boolean tryAdvance(final Consumer<? super T> innerAction) {
-              return spliterator.tryAdvance(
-                  item -> {
-                    action.accept(item);
-                    innerAction.accept(item);
-                  });
-            }
-          };
-
-      return new StreamSource<>(this, peekSpliterator);
-    }
-
-    @Override
-    public Stream<T> limit(long maxSize) {
-      throwIfTerminated();
-      checkState(maxSize >= 0, "maxSize may not be negative");
-      return new StreamSource<>(this, new LimitSpliterator<>(maxSize, spliterator));
-    }
-
-    @Override
-    public Stream<T> skip(long n) {
-      throwIfTerminated();
-      checkState(n >= 0, "n may not be negative");
-      if (n == 0) {
-        return this;
-      }
-      return new StreamSource<>(this, new SkipSpliterator<>(n, spliterator));
-    }
-
-    @Override
-    public boolean isParallel() {
-      throwIfTerminated();
-      return false;
-    }
-
-    @Override
-    public Stream<T> sequential() {
-      throwIfTerminated();
-      return this;
-    }
-
-    @Override
-    public Stream<T> parallel() {
-      throwIfTerminated();
-      // do nothing, no such thing as gwt+parallel
-      return this;
-    }
-
-    @Override
-    public Stream<T> unordered() {
-      throwIfTerminated();
-      return this;
-    }
-  }
 }
diff --git a/user/super/com/google/gwt/emul/java/util/stream/StreamImpl.java b/user/super/com/google/gwt/emul/java/util/stream/StreamImpl.java
new file mode 100644
index 0000000..bb5bc94
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/util/stream/StreamImpl.java
@@ -0,0 +1,991 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package java.util.stream;
+
+import static javaemul.internal.InternalPreconditions.checkNotNull;
+import static javaemul.internal.InternalPreconditions.checkState;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.Spliterators.AbstractDoubleSpliterator;
+import java.util.Spliterators.AbstractIntSpliterator;
+import java.util.Spliterators.AbstractLongSpliterator;
+import java.util.Spliterators.AbstractSpliterator;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.BinaryOperator;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.Function;
+import java.util.function.IntConsumer;
+import java.util.function.IntFunction;
+import java.util.function.LongConsumer;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.ToDoubleFunction;
+import java.util.function.ToIntFunction;
+import java.util.function.ToLongFunction;
+
+/**
+ * Main implementation of Stream, wrapping a single spliterator and an optional parent stream.
+ */
+final class StreamImpl<T> extends TerminatableStream<StreamImpl<T>> implements Stream<T> {
+
+  /**
+   * Represents an empty stream, doing nothing for all methods.
+   */
+  static class Empty<T> extends TerminatableStream<Empty<T>> implements Stream<T> {
+
+    public Empty(TerminatableStream<?> previous) {
+      super(previous);
+    }
+
+    @Override
+    public Stream<T> filter(Predicate<? super T> predicate) {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public <R> Stream<R> map(Function<? super T, ? extends R> mapper) {
+      throwIfTerminated();
+      return (Stream) this;
+    }
+
+    @Override
+    public IntStream mapToInt(ToIntFunction<? super T> mapper) {
+      throwIfTerminated();
+      return new IntStreamImpl.Empty(this);
+    }
+
+    @Override
+    public LongStream mapToLong(ToLongFunction<? super T> mapper) {
+      throwIfTerminated();
+      return new LongStreamImpl.Empty(this);
+    }
+
+    @Override
+    public DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper) {
+      throwIfTerminated();
+      return new DoubleStreamImpl.Empty(this);
+    }
+
+    @Override
+    public <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) {
+      throwIfTerminated();
+      return (Stream) this;
+    }
+
+    @Override
+    public IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) {
+      throwIfTerminated();
+      return new IntStreamImpl.Empty(this);
+    }
+
+    @Override
+    public LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper) {
+      throwIfTerminated();
+      return new LongStreamImpl.Empty(this);
+    }
+
+    @Override
+    public DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper) {
+      throwIfTerminated();
+      return new DoubleStreamImpl.Empty(this);
+    }
+
+    @Override
+    public Stream<T> distinct() {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public Stream<T> sorted() {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public Stream<T> sorted(Comparator<? super T> comparator) {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public Stream<T> peek(Consumer<? super T> action) {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public Stream<T> limit(long maxSize) {
+      throwIfTerminated();
+      checkState(maxSize >= 0, "maxSize may not be negative");
+      return this;
+    }
+
+    @Override
+    public Stream<T> skip(long n) {
+      throwIfTerminated();
+      checkState(n >= 0, "n may not be negative");
+      return this;
+    }
+
+    @Override
+    public void forEach(Consumer<? super T> action) {
+      terminate();
+      // nothing to do
+    }
+
+    @Override
+    public void forEachOrdered(Consumer<? super T> action) {
+      terminate();
+      // nothing to do
+    }
+
+    @Override
+    public Object[] toArray() {
+      terminate();
+      return new Object[0];
+    }
+
+    @Override
+    public <A> A[] toArray(IntFunction<A[]> generator) {
+      terminate();
+      return generator.apply(0);
+    }
+
+    @Override
+    public T reduce(T identity, BinaryOperator<T> accumulator) {
+      terminate();
+      return identity;
+    }
+
+    @Override
+    public Optional<T> reduce(BinaryOperator<T> accumulator) {
+      terminate();
+      return Optional.empty();
+    }
+
+    @Override
+    public <U> U reduce(
+        U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner) {
+      terminate();
+      return identity;
+    }
+
+    @Override
+    public <R> R collect(
+        Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner) {
+      terminate();
+      return supplier.get();
+    }
+
+    @Override
+    public <R, A> R collect(Collector<? super T, A, R> collector) {
+      terminate();
+      return collector.finisher().apply(collector.supplier().get());
+    }
+
+    @Override
+    public Optional<T> min(Comparator<? super T> comparator) {
+      terminate();
+      return Optional.empty();
+    }
+
+    @Override
+    public Optional<T> max(Comparator<? super T> comparator) {
+      terminate();
+      return Optional.empty();
+    }
+
+    @Override
+    public long count() {
+      terminate();
+      return 0;
+    }
+
+    @Override
+    public boolean anyMatch(Predicate<? super T> predicate) {
+      terminate();
+      return false;
+    }
+
+    @Override
+    public boolean allMatch(Predicate<? super T> predicate) {
+      terminate();
+      return true;
+    }
+
+    @Override
+    public boolean noneMatch(Predicate<? super T> predicate) {
+      terminate();
+      return true;
+    }
+
+    @Override
+    public Optional<T> findFirst() {
+      terminate();
+      return Optional.empty();
+    }
+
+    @Override
+    public Optional<T> findAny() {
+      terminate();
+      return Optional.empty();
+    }
+
+    @Override
+    public Iterator<T> iterator() {
+      terminate();
+      return Collections.emptyIterator();
+    }
+
+    @Override
+    public Spliterator<T> spliterator() {
+      terminate();
+      return Spliterators.emptySpliterator();
+    }
+
+    @Override
+    public boolean isParallel() {
+      throwIfTerminated();
+      return false;
+    }
+
+    @Override
+    public Stream<T> sequential() {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public Stream<T> parallel() {
+      throwIfTerminated();
+      return this;
+    }
+
+    @Override
+    public Stream<T> unordered() {
+      throwIfTerminated();
+      return this;
+    }
+  }
+
+  /**
+   * Object to Object map spliterator.
+   *
+   * @param <U> the input type
+   * @param <T> the output type
+   */
+  private static final class MapToObjSpliterator<U, T> extends Spliterators.AbstractSpliterator<T> {
+    private final Function<? super U, ? extends T> map;
+    private final Spliterator<U> original;
+
+    public MapToObjSpliterator(Function<? super U, ? extends T> map, Spliterator<U> original) {
+      super(
+          original.estimateSize(),
+          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
+      checkNotNull(map);
+      this.map = map;
+      this.original = original;
+    }
+
+    @Override
+    public boolean tryAdvance(final Consumer<? super T> action) {
+      return original.tryAdvance(u -> action.accept(map.apply(u)));
+    }
+  }
+
+  /**
+   * Object to Int map spliterator.
+   *
+   * @param <T> the input type
+   */
+  private static final class MapToIntSpliterator<T> extends Spliterators.AbstractIntSpliterator {
+    private final ToIntFunction<? super T> map;
+    private final Spliterator<T> original;
+
+    public MapToIntSpliterator(ToIntFunction<? super T> map, Spliterator<T> original) {
+      super(
+          original.estimateSize(),
+          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
+      checkNotNull(map);
+      this.map = map;
+      this.original = original;
+    }
+
+    @Override
+    public boolean tryAdvance(final IntConsumer action) {
+      return original.tryAdvance(u -> action.accept(map.applyAsInt(u)));
+    }
+  }
+
+  /**
+   * Object to Long map spliterator.
+   *
+   * @param <T> the input type
+   */
+  private static final class MapToLongSpliterator<T> extends Spliterators.AbstractLongSpliterator {
+    private final ToLongFunction<? super T> map;
+    private final Spliterator<T> original;
+
+    public MapToLongSpliterator(ToLongFunction<? super T> map, Spliterator<T> original) {
+      super(
+          original.estimateSize(),
+          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
+      checkNotNull(map);
+      this.map = map;
+      this.original = original;
+    }
+
+    @Override
+    public boolean tryAdvance(final LongConsumer action) {
+      return original.tryAdvance(u -> action.accept(map.applyAsLong(u)));
+    }
+  }
+
+  /**
+   * Object to Double map spliterator.
+   *
+   * @param <T> the input type
+   */
+  private static final class MapToDoubleSpliterator<T>
+      extends Spliterators.AbstractDoubleSpliterator {
+    private final ToDoubleFunction<? super T> map;
+    private final Spliterator<T> original;
+
+    public MapToDoubleSpliterator(ToDoubleFunction<? super T> map, Spliterator<T> original) {
+      super(
+          original.estimateSize(),
+          original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT));
+      checkNotNull(map);
+      this.map = map;
+      this.original = original;
+    }
+
+    @Override
+    public boolean tryAdvance(final DoubleConsumer action) {
+      return original.tryAdvance(u -> action.accept(map.applyAsDouble(u)));
+    }
+  }
+
+  /**
+   * Object filter spliterator.
+   *
+   * @param <T> the type of data to iterate over
+   */
+  private static final class FilterSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
+    private final Predicate<? super T> filter;
+    private final Spliterator<T> original;
+
+    private boolean found;
+
+    public FilterSpliterator(Predicate<? super T> filter, Spliterator<T> original) {
+      super(original.estimateSize(), original.characteristics() & ~Spliterator.SIZED);
+      checkNotNull(filter);
+      this.filter = filter;
+      this.original = original;
+    }
+
+    @Override
+    public Comparator<? super T> getComparator() {
+      return original.getComparator();
+    }
+
+    @Override
+    public boolean tryAdvance(final Consumer<? super T> action) {
+      found = false;
+      while (!found
+          && original.tryAdvance(
+              item -> {
+                if (filter.test(item)) {
+                  found = true;
+                  action.accept(item);
+                }
+              })) {
+        // do nothing, work is done in tryAdvance
+      }
+
+      return found;
+    }
+  }
+
+  /**
+   * Object skip spliterator.
+   *
+   * @param <T> the type of data to iterate over
+   */
+  private static final class SkipSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
+    private long skip;
+    private final Spliterator<T> original;
+
+    public SkipSpliterator(long skip, Spliterator<T> original) {
+      super(
+          original.hasCharacteristics(Spliterator.SIZED)
+              ? Math.max(0, original.estimateSize() - skip)
+              : Long.MAX_VALUE,
+          original.characteristics());
+      this.skip = skip;
+      this.original = original;
+    }
+
+    @Override
+    public Comparator<? super T> getComparator() {
+      return original.getComparator();
+    }
+
+    @Override
+    public boolean tryAdvance(Consumer<? super T> action) {
+      while (skip > 0) {
+        if (!original.tryAdvance(ignore -> { })) {
+          return false;
+        }
+        skip--;
+      }
+      return original.tryAdvance(action);
+    }
+  }
+
+  /**
+   * Object limit spliterator.
+   *
+   * @param <T> the type of data to iterate over
+   */
+  private static final class LimitSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
+    private final long limit;
+    private final Spliterator<T> original;
+    private int position = 0;
+
+    public LimitSpliterator(long limit, Spliterator<T> original) {
+      super(
+          original.hasCharacteristics(Spliterator.SIZED)
+              ? Math.min(original.estimateSize(), limit)
+              : Long.MAX_VALUE,
+          original.characteristics());
+      this.limit = limit;
+      this.original = original;
+    }
+
+    @Override
+    public Comparator<? super T> getComparator() {
+      return original.getComparator();
+    }
+
+    @Override
+    public boolean tryAdvance(Consumer<? super T> action) {
+      if (position >= limit) {
+        return false;
+      }
+      boolean result = original.tryAdvance(action);
+      position++;
+      return result;
+    }
+  }
+
+  /**
+   * Value holder for various stream operations.
+   */
+  private static final class ValueConsumer<T> implements Consumer<T> {
+    T value;
+
+    @Override
+    public void accept(T value) {
+      this.value = value;
+    }
+  }
+
+  private final Spliterator<T> spliterator;
+
+  public StreamImpl(TerminatableStream<?> prev, Spliterator<T> spliterator) {
+    super(prev);
+    this.spliterator = spliterator;
+  }
+
+  // terminal
+  @Override
+  public Spliterator<T> spliterator() {
+    terminate();
+    return spliterator;
+  }
+
+  @Override
+  public Iterator<T> iterator() {
+    return Spliterators.iterator(spliterator());
+  }
+
+  @Override
+  public long count() {
+    terminate();
+    long count = 0;
+    while (spliterator.tryAdvance(a -> { })) {
+      count++;
+    }
+    return count;
+  }
+
+  @Override
+  public void forEach(Consumer<? super T> action) {
+    forEachOrdered(action);
+  }
+
+  @Override
+  public void forEachOrdered(Consumer<? super T> action) {
+    terminate();
+    spliterator.forEachRemaining(action);
+  }
+
+  @Override
+  public Object[] toArray() {
+    return toArray(Object[]::new);
+  }
+
+  @Override
+  public <A> A[] toArray(IntFunction<A[]> generator) {
+    List<T> collected = collect(Collectors.toList());
+    return collected.toArray(generator.apply(collected.size()));
+  }
+
+  @Override
+  public <R> R collect(
+      Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner) {
+    return collect(
+        Collector.of(
+            supplier,
+            accumulator,
+            (a, b) -> {
+              combiner.accept(a, b);
+              return a;
+            }));
+  }
+
+  @Override
+  public <R, A> R collect(final Collector<? super T, A, R> collector) {
+    return collector
+        .finisher()
+        .apply(
+            reduce(
+                collector.supplier().get(),
+                (a, t) -> {
+                  collector.accumulator().accept(a, t);
+                  return a;
+                },
+                collector.combiner()));
+  }
+
+  @Override
+  public Optional<T> findFirst() {
+    terminate();
+    ValueConsumer<T> holder = new ValueConsumer<T>();
+    if (spliterator.tryAdvance(holder)) {
+      return Optional.of(holder.value);
+    }
+    return Optional.empty();
+  }
+
+  @Override
+  public Optional<T> findAny() {
+    return findFirst();
+  }
+
+  @Override
+  public boolean anyMatch(Predicate<? super T> predicate) {
+    return filter(predicate).findFirst().isPresent();
+  }
+
+  @Override
+  public boolean allMatch(final Predicate<? super T> predicate) {
+    return !anyMatch(predicate.negate());
+  }
+
+  @Override
+  public boolean noneMatch(final Predicate<? super T> predicate) {
+    return !anyMatch(predicate);
+  }
+
+  @Override
+  public Optional<T> min(final Comparator<? super T> comparator) {
+    return reduce(BinaryOperator.minBy(comparator));
+  }
+
+  @Override
+  public Optional<T> max(final Comparator<? super T> comparator) {
+    return reduce(BinaryOperator.maxBy(comparator));
+  }
+
+  @Override
+  public T reduce(T identity, BinaryOperator<T> accumulator) {
+    return reduce(identity, accumulator, accumulator);
+  }
+
+  @Override
+  public Optional<T> reduce(BinaryOperator<T> accumulator) {
+    ValueConsumer<T> consumer = new ValueConsumer<T>();
+    if (!spliterator.tryAdvance(consumer)) {
+      terminate();
+      return Optional.empty();
+    }
+    return Optional.of(reduce(consumer.value, accumulator));
+  }
+
+  // combiner is ignored, since we don't parallelize
+  @Override
+  public <U> U reduce(
+      U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner) {
+    terminate();
+    final ValueConsumer<U> consumer = new ValueConsumer<U>();
+    consumer.value = identity;
+    spliterator.forEachRemaining(
+        item -> {
+          consumer.accept(accumulator.apply(consumer.value, item));
+        });
+    return consumer.value;
+  }
+  // end terminal
+
+  // intermediate
+  @Override
+  public Stream<T> filter(Predicate<? super T> predicate) {
+    throwIfTerminated();
+    return new StreamImpl<>(this, new FilterSpliterator<>(predicate, spliterator));
+  }
+
+  @Override
+  public <R> Stream<R> map(Function<? super T, ? extends R> mapper) {
+    throwIfTerminated();
+    return new StreamImpl<>(this, new MapToObjSpliterator<>(mapper, spliterator));
+  }
+
+  @Override
+  public IntStream mapToInt(ToIntFunction<? super T> mapper) {
+    throwIfTerminated();
+    return new IntStreamImpl(this, new MapToIntSpliterator<>(mapper, spliterator));
+  }
+
+  @Override
+  public LongStream mapToLong(ToLongFunction<? super T> mapper) {
+    throwIfTerminated();
+    return new LongStreamImpl(this, new MapToLongSpliterator<>(mapper, spliterator));
+  }
+
+  @Override
+  public DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper) {
+    throwIfTerminated();
+    return new DoubleStreamImpl(this, new MapToDoubleSpliterator<>(mapper, spliterator));
+  }
+
+  @Override
+  public <R> Stream<R> flatMap(final Function<? super T, ? extends Stream<? extends R>> mapper) {
+    throwIfTerminated();
+    final Spliterator<? extends Stream<? extends R>> spliteratorOfStreams =
+        new MapToObjSpliterator<>(mapper, spliterator);
+
+    AbstractSpliterator<R> flatMapSpliterator =
+        new Spliterators.AbstractSpliterator<R>(Long.MAX_VALUE, 0) {
+          Stream<? extends R> nextStream;
+          Spliterator<? extends R> next;
+
+          @Override
+          public boolean tryAdvance(Consumer<? super R> action) {
+            // look for a new spliterator
+            while (advanceToNextSpliterator()) {
+              // if we have one, try to read and use it
+              if (next.tryAdvance(action)) {
+                return true;
+              } else {
+                nextStream.close();
+                nextStream = null;
+                // failed, null it out so we can find another
+                next = null;
+              }
+            }
+            return false;
+          }
+
+          private boolean advanceToNextSpliterator() {
+            while (next == null) {
+              if (!spliteratorOfStreams.tryAdvance(
+                  n -> {
+                    if (n != null) {
+                      nextStream = n;
+                      next = n.spliterator();
+                    }
+                  })) {
+                return false;
+              }
+            }
+            return true;
+          }
+        };
+
+    return new StreamImpl<R>(this, flatMapSpliterator);
+  }
+
+  @Override
+  public IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) {
+    throwIfTerminated();
+    final Spliterator<? extends IntStream> spliteratorOfStreams =
+        new MapToObjSpliterator<>(mapper, spliterator);
+
+    AbstractIntSpliterator flatMapSpliterator =
+        new Spliterators.AbstractIntSpliterator(Long.MAX_VALUE, 0) {
+          IntStream nextStream;
+          Spliterator.OfInt next;
+
+          @Override
+          public boolean tryAdvance(IntConsumer action) {
+            // look for a new spliterator
+            while (advanceToNextSpliterator()) {
+              // if we have one, try to read and use it
+              if (next.tryAdvance(action)) {
+                return true;
+              } else {
+                nextStream.close();
+                nextStream = null;
+                // failed, null it out so we can find another
+                next = null;
+              }
+            }
+            return false;
+          }
+
+          private boolean advanceToNextSpliterator() {
+            while (next == null) {
+              if (!spliteratorOfStreams.tryAdvance(
+                  n -> {
+                    if (n != null) {
+                      nextStream = n;
+                      next = n.spliterator();
+                    }
+                  })) {
+                return false;
+              }
+            }
+            return true;
+          }
+        };
+
+    return new IntStreamImpl(this, flatMapSpliterator);
+  }
+
+  @Override
+  public LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper) {
+    throwIfTerminated();
+    final Spliterator<? extends LongStream> spliteratorOfStreams =
+        new MapToObjSpliterator<>(mapper, spliterator);
+
+    AbstractLongSpliterator flatMapSpliterator =
+        new Spliterators.AbstractLongSpliterator(Long.MAX_VALUE, 0) {
+          LongStream nextStream;
+          Spliterator.OfLong next;
+
+          @Override
+          public boolean tryAdvance(LongConsumer action) {
+            // look for a new spliterator
+            while (advanceToNextSpliterator()) {
+              // if we have one, try to read and use it
+              if (next.tryAdvance(action)) {
+                return true;
+              } else {
+                nextStream.close();
+                nextStream = null;
+                // failed, null it out so we can find another
+                next = null;
+              }
+            }
+            return false;
+          }
+
+          private boolean advanceToNextSpliterator() {
+            while (next == null) {
+              if (!spliteratorOfStreams.tryAdvance(
+                  n -> {
+                    if (n != null) {
+                      nextStream = n;
+                      next = n.spliterator();
+                    }
+                  })) {
+                return false;
+              }
+            }
+            return true;
+          }
+        };
+
+    return new LongStreamImpl(this, flatMapSpliterator);
+  }
+
+  @Override
+  public DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper) {
+    throwIfTerminated();
+    final Spliterator<? extends DoubleStream> spliteratorOfStreams =
+        new MapToObjSpliterator<>(mapper, spliterator);
+
+    AbstractDoubleSpliterator flatMapSpliterator =
+        new Spliterators.AbstractDoubleSpliterator(Long.MAX_VALUE, 0) {
+          DoubleStream nextStream;
+          Spliterator.OfDouble next;
+
+          @Override
+          public boolean tryAdvance(DoubleConsumer action) {
+            // look for a new spliterator
+            while (advanceToNextSpliterator()) {
+              // if we have one, try to read and use it
+              if (next.tryAdvance(action)) {
+                return true;
+              } else {
+                nextStream.close();
+                nextStream = null;
+                // failed, null it out so we can find another
+                next = null;
+              }
+            }
+            return false;
+          }
+
+          private boolean advanceToNextSpliterator() {
+            while (next == null) {
+              if (!spliteratorOfStreams.tryAdvance(
+                  n -> {
+                    if (n != null) {
+                      nextStream = n;
+                      next = n.spliterator();
+                    }
+                  })) {
+                return false;
+              }
+            }
+            return true;
+          }
+        };
+
+    return new DoubleStreamImpl(this, flatMapSpliterator);
+  }
+
+  @Override
+  public Stream<T> distinct() {
+    throwIfTerminated();
+    HashSet<T> seen = new HashSet<>();
+    return filter(seen::add);
+  }
+
+  @Override
+  public Stream<T> sorted() {
+    throwIfTerminated();
+    Comparator<T> c = (Comparator) Comparator.naturalOrder();
+    return sorted(c);
+  }
+
+  @Override
+  public Stream<T> sorted(final Comparator<? super T> comparator) {
+    throwIfTerminated();
+
+    AbstractSpliterator<T> sortedSpliterator =
+        new Spliterators.AbstractSpliterator<T>(
+            spliterator.estimateSize(), spliterator.characteristics() | Spliterator.SORTED) {
+          Spliterator<T> ordered = null;
+
+          @Override
+          public Comparator<? super T> getComparator() {
+            return comparator == Comparator.naturalOrder() ? null : comparator;
+          }
+
+          @Override
+          public boolean tryAdvance(Consumer<? super T> action) {
+            if (ordered == null) {
+              List<T> list = new ArrayList<>();
+              spliterator.forEachRemaining(list::add);
+              Collections.sort(list, comparator);
+              ordered = list.spliterator();
+            }
+            return ordered.tryAdvance(action);
+          }
+        };
+
+    return new StreamImpl<>(this, sortedSpliterator);
+  }
+
+  @Override
+  public Stream<T> peek(final Consumer<? super T> action) {
+    checkNotNull(action);
+    throwIfTerminated();
+
+    AbstractSpliterator<T> peekSpliterator =
+        new Spliterators.AbstractSpliterator<T>(
+            spliterator.estimateSize(), spliterator.characteristics()) {
+          @Override
+          public boolean tryAdvance(final Consumer<? super T> innerAction) {
+            return spliterator.tryAdvance(
+                item -> {
+                  action.accept(item);
+                  innerAction.accept(item);
+                });
+          }
+        };
+
+    return new StreamImpl<>(this, peekSpliterator);
+  }
+
+  @Override
+  public Stream<T> limit(long maxSize) {
+    throwIfTerminated();
+    checkState(maxSize >= 0, "maxSize may not be negative");
+    return new StreamImpl<>(this, new LimitSpliterator<>(maxSize, spliterator));
+  }
+
+  @Override
+  public Stream<T> skip(long n) {
+    throwIfTerminated();
+    checkState(n >= 0, "n may not be negative");
+    if (n == 0) {
+      return this;
+    }
+    return new StreamImpl<>(this, new SkipSpliterator<>(n, spliterator));
+  }
+
+  @Override
+  public boolean isParallel() {
+    throwIfTerminated();
+    return false;
+  }
+
+  @Override
+  public Stream<T> sequential() {
+    throwIfTerminated();
+    return this;
+  }
+
+  @Override
+  public Stream<T> parallel() {
+    throwIfTerminated();
+    // do nothing, no such thing as gwt+parallel
+    return this;
+  }
+
+  @Override
+  public Stream<T> unordered() {
+    throwIfTerminated();
+    return this;
+  }
+}
diff --git a/user/super/com/google/gwt/emul/java/util/stream/StreamSupport.java b/user/super/com/google/gwt/emul/java/util/stream/StreamSupport.java
index 0c64d9e..3b37063 100644
--- a/user/super/com/google/gwt/emul/java/util/stream/StreamSupport.java
+++ b/user/super/com/google/gwt/emul/java/util/stream/StreamSupport.java
@@ -13,7 +13,6 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package java.util.stream;
 
 import java.util.Spliterator;
@@ -26,7 +25,7 @@
 public final class StreamSupport {
 
   public static DoubleStream doubleStream(Spliterator.OfDouble spliterator, boolean parallel) {
-    return new DoubleStream.DoubleStreamSource(null, spliterator);
+    return new DoubleStreamImpl(null, spliterator);
   }
 
   public static DoubleStream doubleStream(
@@ -41,7 +40,7 @@
   }
 
   public static IntStream intStream(Spliterator.OfInt spliterator, boolean parallel) {
-    return new IntStream.IntStreamSource(null, spliterator);
+    return new IntStreamImpl(null, spliterator);
   }
 
   public static IntStream intStream(
@@ -56,7 +55,7 @@
   }
 
   public static LongStream longStream(Spliterator.OfLong spliterator, boolean parallel) {
-    return new LongStream.LongStreamSource(null, spliterator);
+    return new LongStreamImpl(null, spliterator);
   }
 
   public static LongStream longStream(
@@ -73,7 +72,7 @@
   }
 
   public static <T> Stream<T> stream(Spliterator<T> spliterator, boolean parallel) {
-    return new Stream.StreamSource<T>(null, spliterator);
+    return new StreamImpl<T>(null, spliterator);
   }
 
   public static <T> Stream<T> stream(