Nits in streams.

Change-Id: I61c4d675aded551f80afc97e1d52adc81132f7e9
diff --git a/user/super/com/google/gwt/emul/java/util/stream/BaseStream.java b/user/super/com/google/gwt/emul/java/util/stream/BaseStream.java
index 66a5a7c..3692d8c 100644
--- a/user/super/com/google/gwt/emul/java/util/stream/BaseStream.java
+++ b/user/super/com/google/gwt/emul/java/util/stream/BaseStream.java
@@ -41,5 +41,6 @@
 
   S onClose(Runnable closeHandler);
 
+  @Override
   void close();
 }
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 99c0771..bfe782c 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
@@ -16,7 +16,6 @@
 
 package java.util.stream;
 
-import static javaemul.internal.InternalPreconditions.checkCriticalState;
 import static javaemul.internal.InternalPreconditions.checkNotNull;
 import static javaemul.internal.InternalPreconditions.checkState;
 
@@ -73,7 +72,7 @@
 
       @Override
       public DoubleStream build() {
-        checkCriticalState(items != null, "Builder already built");
+        checkState(items != null, "Builder already built");
         DoubleStream stream = Arrays.stream(items);
         items = null;
         return stream;
@@ -90,44 +89,42 @@
     // TODO replace this flatMap-ish spliterator with one that directly combines the two root
     // streams
     Spliterator<? extends DoubleStream> spliteratorOfStreams = Arrays.asList(a, b).spliterator();
-    DoubleStream result =
-        new DoubleStreamSource(
-            null,
-            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 = null;
-                    // failed, null it out so we can find another
-                    next = null;
-                  }
-                }
+    Spliterator.OfDouble spliterator =
+        new Spliterators.AbstractDoubleSpliterator(Long.MAX_VALUE, 0) {
+          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 {
+                // 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) {
+                      next = n.spliterator();
+                    }
+                  })) {
                 return false;
               }
+            }
+            return true;
+          }
+        };
 
-              private boolean advanceToNextSpliterator() {
-                while (next == null) {
-                  if (!spliteratorOfStreams.tryAdvance(
-                      n -> {
-                        if (n != null) {
-                          nextStream = n;
-                          next = n.spliterator();
-                        }
-                      })) {
-                    return false;
-                  }
-                }
-                return true;
-              }
-            });
+    DoubleStream result = new DoubleStreamSource(null, spliterator);
 
     result.onClose(a::close);
     result.onClose(b::close);
@@ -140,7 +137,7 @@
   }
 
   static DoubleStream generate(DoubleSupplier s) {
-    return StreamSupport.doubleStream(
+    Spliterator.OfDouble spliterator =
         new Spliterators.AbstractDoubleSpliterator(
             Long.MAX_VALUE, Spliterator.IMMUTABLE | Spliterator.ORDERED) {
           @Override
@@ -148,12 +145,13 @@
             action.accept(s.getAsDouble());
             return true;
           }
-        },
-        false);
+        };
+
+    return StreamSupport.doubleStream(spliterator, false);
   }
 
   static DoubleStream iterate(double seed, DoubleUnaryOperator f) {
-    return StreamSupport.doubleStream(
+    Spliterator.OfDouble spliterator =
         new Spliterators.AbstractDoubleSpliterator(
             Long.MAX_VALUE, Spliterator.IMMUTABLE | Spliterator.ORDERED) {
           private double next = seed;
@@ -164,8 +162,9 @@
             next = f.applyAsDouble(next);
             return true;
           }
-        },
-        false);
+        };
+
+    return StreamSupport.doubleStream(spliterator, false);
   }
 
   static DoubleStream of(double... values) {
@@ -220,6 +219,7 @@
 
   void forEachOrdered(DoubleConsumer action);
 
+  @Override
   PrimitiveIterator.OfDouble iterator();
 
   DoubleStream limit(long maxSize);
@@ -238,6 +238,7 @@
 
   boolean noneMatch(DoublePredicate predicate);
 
+  @Override
   DoubleStream parallel();
 
   DoubleStream peek(DoubleConsumer action);
@@ -246,12 +247,14 @@
 
   double reduce(double identity, DoubleBinaryOperator op);
 
+  @Override
   DoubleStream sequential();
 
   DoubleStream skip(long n);
 
   DoubleStream sorted();
 
+  @Override
   Spliterator.OfDouble spliterator();
 
   double sum();
@@ -669,9 +672,10 @@
 
     @Override
     public boolean tryAdvance(DoubleConsumer action) {
-      if (limit <= position) {
+      if (position >= limit) {
         return false;
       }
+
       boolean result = original.tryAdvance(action);
       position++;
       return result;
@@ -752,30 +756,20 @@
 
     @Override
     public OptionalDouble min() {
-      terminate();
-      final ValueConsumer holder = new ValueConsumer();
-      if (spliterator.tryAdvance((double value) -> holder.value = value)) {
-        spliterator.forEachRemaining(
-            (double value) -> {
-              holder.value = Math.min(holder.value, value);
-            });
-        return OptionalDouble.of(holder.value);
+      DoubleSummaryStatistics stats = summaryStatistics();
+      if (stats.getCount() == 0) {
+        return OptionalDouble.empty();
       }
-      return OptionalDouble.empty();
+      return OptionalDouble.of(stats.getMin());
     }
 
     @Override
     public OptionalDouble max() {
-      terminate();
-      final ValueConsumer holder = new ValueConsumer();
-      if (spliterator.tryAdvance((double value) -> holder.value = value)) {
-        spliterator.forEachRemaining(
-            (double value) -> {
-              holder.value = Math.max(holder.value, value);
-            });
-        return OptionalDouble.of(holder.value);
+      DoubleSummaryStatistics stats = summaryStatistics();
+      if (stats.getCount() == 0) {
+        return OptionalDouble.empty();
       }
-      return OptionalDouble.empty();
+      return OptionalDouble.of(stats.getMax());
     }
 
     @Override
@@ -866,7 +860,7 @@
     @Override
     public <U> Stream<U> mapToObj(DoubleFunction<? extends U> mapper) {
       throwIfTerminated();
-      return new Stream.StreamSource(this, new MapToObjSpliterator<U>(mapper, spliterator));
+      return new Stream.StreamSource<U>(this, new MapToObjSpliterator<U>(mapper, spliterator));
     }
 
     @Override
@@ -886,8 +880,8 @@
       throwIfTerminated();
       final Spliterator<? extends DoubleStream> spliteratorOfStreams =
           new MapToObjSpliterator<DoubleStream>(mapper, spliterator);
-      return new DoubleStreamSource(
-          this,
+
+      Spliterator.OfDouble flatMapSpliterator =
           new Spliterators.AbstractDoubleSpliterator(Long.MAX_VALUE, 0) {
             DoubleStream nextStream;
             Spliterator.OfDouble next;
@@ -923,7 +917,9 @@
               }
               return true;
             }
-          });
+          };
+
+      return new DoubleStreamSource(this, flatMapSpliterator);
     }
 
     @Override
@@ -936,8 +932,8 @@
     @Override
     public DoubleStream sorted() {
       throwIfTerminated();
-      return new DoubleStreamSource(
-          this,
+
+      Spliterator.OfDouble sortingSpliterator =
           new Spliterators.AbstractDoubleSpliterator(
               spliterator.estimateSize(), spliterator.characteristics() | Spliterator.SORTED) {
             Spliterator.OfDouble ordered = null;
@@ -957,22 +953,26 @@
               }
               return ordered.tryAdvance(action);
             }
-          });
+          };
+
+      return new DoubleStreamSource(this, sortingSpliterator);
     }
 
     @Override
     public DoubleStream peek(DoubleConsumer action) {
       checkNotNull(action);
       throwIfTerminated();
-      return new DoubleStreamSource(
-          this,
+
+      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
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 c6c42e9..1a2b3fd 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
@@ -16,7 +16,6 @@
 
 package java.util.stream;
 
-import static javaemul.internal.InternalPreconditions.checkCriticalState;
 import static javaemul.internal.InternalPreconditions.checkNotNull;
 import static javaemul.internal.InternalPreconditions.checkState;
 
@@ -29,6 +28,7 @@
 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;
@@ -74,7 +74,7 @@
 
       @Override
       public IntStream build() {
-        checkCriticalState(items != null, "Builder already built");
+        checkState(items != null, "Builder already built");
         IntStream stream = Arrays.stream(items);
         items = null;
         return stream;
@@ -91,44 +91,42 @@
     // TODO replace this flatMap-ish spliterator with one that directly combines the two root
     // streams
     Spliterator<? extends IntStream> spliteratorOfStreams = Arrays.asList(a, b).spliterator();
-    IntStream result =
-        new IntStreamSource(
-            null,
-            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 = null;
-                    // failed, null it out so we can find another
-                    next = null;
-                  }
-                }
+    Spliterator.OfInt spliterator =
+        new Spliterators.AbstractIntSpliterator(Long.MAX_VALUE, 0) {
+          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 {
+                // 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) {
+                      next = n.spliterator();
+                    }
+                  })) {
                 return false;
               }
+            }
+            return true;
+          }
+        };
 
-              private boolean advanceToNextSpliterator() {
-                while (next == null) {
-                  if (!spliteratorOfStreams.tryAdvance(
-                      n -> {
-                        if (n != null) {
-                          nextStream = n;
-                          next = n.spliterator();
-                        }
-                      })) {
-                    return false;
-                  }
-                }
-                return true;
-              }
-            });
+    IntStream result = new IntStreamSource(null, spliterator);
 
     result.onClose(a::close);
     result.onClose(b::close);
@@ -141,7 +139,7 @@
   }
 
   static IntStream generate(final IntSupplier s) {
-    return StreamSupport.intStream(
+    AbstractIntSpliterator spliterator =
         new Spliterators.AbstractIntSpliterator(
             Long.MAX_VALUE, Spliterator.IMMUTABLE | Spliterator.ORDERED) {
           @Override
@@ -149,12 +147,14 @@
             action.accept(s.getAsInt());
             return true;
           }
-        },
-        false);
+        };
+
+    return StreamSupport.intStream(spliterator, false);
   }
 
   static IntStream iterate(int seed, IntUnaryOperator f) {
-    return StreamSupport.intStream(
+
+    AbstractIntSpliterator spliterator =
         new Spliterators.AbstractIntSpliterator(
             Long.MAX_VALUE, Spliterator.IMMUTABLE | Spliterator.ORDERED) {
           private int next = seed;
@@ -165,8 +165,9 @@
             next = f.applyAsInt(next);
             return true;
           }
-        },
-        false);
+        };
+
+    return StreamSupport.intStream(spliterator, false);
   }
 
   static IntStream of(int... values) {
@@ -191,7 +192,7 @@
     }
     int count = endInclusive - startInclusive + 1;
 
-    return StreamSupport.intStream(
+    AbstractIntSpliterator spliterator =
         new Spliterators.AbstractIntSpliterator(
             count,
             Spliterator.IMMUTABLE
@@ -215,8 +216,9 @@
             }
             return false;
           }
-        },
-        false);
+        };
+
+    return StreamSupport.intStream(spliterator, false);
   }
 
   /**
@@ -266,6 +268,7 @@
 
   void forEachOrdered(IntConsumer action);
 
+  @Override
   PrimitiveIterator.OfInt iterator();
 
   IntStream limit(long maxSize);
@@ -284,6 +287,7 @@
 
   boolean noneMatch(IntPredicate predicate);
 
+  @Override
   IntStream parallel();
 
   IntStream peek(IntConsumer action);
@@ -292,12 +296,14 @@
 
   int reduce(int identity, IntBinaryOperator op);
 
+  @Override
   IntStream sequential();
 
   IntStream skip(long n);
 
   IntStream sorted();
 
+  @Override
   Spliterator.OfInt spliterator();
 
   int sum();
@@ -727,7 +733,7 @@
 
     @Override
     public boolean tryAdvance(IntConsumer action) {
-      if (limit <= position) {
+      if (position >= limit) {
         return false;
       }
       boolean result = original.tryAdvance(action);
@@ -819,24 +825,20 @@
 
     @Override
     public OptionalInt max() {
-      terminate();
-      final ValueConsumer holder = new ValueConsumer();
-      if (spliterator.tryAdvance((int value) -> holder.value = value)) {
-        spliterator.forEachRemaining((int value) -> holder.value = Math.max(holder.value, value));
-        return OptionalInt.of(holder.value);
+      IntSummaryStatistics stats = summaryStatistics();
+      if (stats.getCount() == 0) {
+        return OptionalInt.empty();
       }
-      return OptionalInt.empty();
+      return OptionalInt.of(stats.getMax());
     }
 
     @Override
     public OptionalInt min() {
-      terminate();
-      final ValueConsumer holder = new ValueConsumer();
-      if (spliterator.tryAdvance((int value) -> holder.value = value)) {
-        spliterator.forEachRemaining((int value) -> holder.value = Math.min(holder.value, value));
-        return OptionalInt.of(holder.value);
+      IntSummaryStatistics stats = summaryStatistics();
+      if (stats.getCount() == 0) {
+        return OptionalInt.empty();
       }
-      return OptionalInt.empty();
+      return OptionalInt.of(stats.getMin());
     }
 
     @Override
@@ -914,7 +916,7 @@
     @Override
     public <U> Stream<U> mapToObj(IntFunction<? extends U> mapper) {
       throwIfTerminated();
-      return new Stream.StreamSource(this, new MapToObjSpliterator<U>(mapper, spliterator));
+      return new Stream.StreamSource<U>(this, new MapToObjSpliterator<U>(mapper, spliterator));
     }
 
     @Override
@@ -935,8 +937,8 @@
       throwIfTerminated();
       final Spliterator<? extends IntStream> spliteratorOfStreams =
           new MapToObjSpliterator<>(mapper, spliterator);
-      return new IntStreamSource(
-          this,
+
+      Spliterator.OfInt flatMapSpliterator =
           new Spliterators.AbstractIntSpliterator(Long.MAX_VALUE, 0) {
             IntStream nextStream;
             Spliterator.OfInt next;
@@ -972,7 +974,9 @@
               }
               return true;
             }
-          });
+          };
+
+      return new IntStreamSource(this, flatMapSpliterator);
     }
 
     @Override
@@ -985,8 +989,8 @@
     @Override
     public IntStream sorted() {
       throwIfTerminated();
-      return new IntStreamSource(
-          this,
+
+      AbstractIntSpliterator sortedSpliterator =
           new Spliterators.AbstractIntSpliterator(
               spliterator.estimateSize(), spliterator.characteristics() | Spliterator.SORTED) {
             Spliterator.OfInt ordered = null;
@@ -1006,22 +1010,26 @@
               }
               return ordered.tryAdvance(action);
             }
-          });
+          };
+
+      return new IntStreamSource(this, sortedSpliterator);
     }
 
     @Override
     public IntStream peek(IntConsumer action) {
       checkNotNull(action);
       throwIfTerminated();
-      return new IntStreamSource(
-          this,
+
+      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
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 9f354b7..6981225 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
@@ -16,7 +16,6 @@
 
 package java.util.stream;
 
-import static javaemul.internal.InternalPreconditions.checkCriticalState;
 import static javaemul.internal.InternalPreconditions.checkNotNull;
 import static javaemul.internal.InternalPreconditions.checkState;
 
@@ -29,6 +28,7 @@
 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;
@@ -74,7 +74,7 @@
 
       @Override
       public LongStream build() {
-        checkCriticalState(items != null, "Builder already built");
+        checkState(items != null, "Builder already built");
         LongStream stream = Arrays.stream(items);
         items = null;
         return stream;
@@ -91,44 +91,42 @@
     // TODO replace this flatMap-ish spliterator with one that directly combines the two root
     // streams
     Spliterator<? extends LongStream> spliteratorOfStreams = Arrays.asList(a, b).spliterator();
-    LongStream result =
-        new LongStreamSource(
-            null,
-            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 = null;
-                    // failed, null it out so we can find another
-                    next = null;
-                  }
-                }
+    AbstractLongSpliterator spliterator =
+        new Spliterators.AbstractLongSpliterator(Long.MAX_VALUE, 0) {
+          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 {
+                // 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) {
+                      next = n.spliterator();
+                    }
+                  })) {
                 return false;
               }
+            }
+            return true;
+          }
+        };
 
-              private boolean advanceToNextSpliterator() {
-                while (next == null) {
-                  if (!spliteratorOfStreams.tryAdvance(
-                      n -> {
-                        if (n != null) {
-                          nextStream = n;
-                          next = n.spliterator();
-                        }
-                      })) {
-                    return false;
-                  }
-                }
-                return true;
-              }
-            });
+    LongStream result = new LongStreamSource(null, spliterator);
 
     result.onClose(a::close);
     result.onClose(b::close);
@@ -141,7 +139,7 @@
   }
 
   static LongStream generate(LongSupplier s) {
-    return StreamSupport.longStream(
+    AbstractLongSpliterator spltierator =
         new Spliterators.AbstractLongSpliterator(
             Long.MAX_VALUE, Spliterator.IMMUTABLE | Spliterator.ORDERED) {
           @Override
@@ -149,12 +147,13 @@
             action.accept(s.getAsLong());
             return true;
           }
-        },
-        false);
+        };
+
+    return StreamSupport.longStream(spltierator, false);
   }
 
   static LongStream iterate(long seed, LongUnaryOperator f) {
-    return StreamSupport.longStream(
+    AbstractLongSpliterator spliterator =
         new Spliterators.AbstractLongSpliterator(
             Long.MAX_VALUE, Spliterator.IMMUTABLE | Spliterator.ORDERED) {
           private long next = seed;
@@ -165,8 +164,8 @@
             next = f.applyAsLong(next);
             return true;
           }
-        },
-        false);
+        };
+    return StreamSupport.longStream(spliterator, false);
   }
 
   static LongStream of(long... values) {
@@ -191,7 +190,7 @@
     }
     long count = endInclusive - startInclusive + 1;
 
-    return StreamSupport.longStream(
+    AbstractLongSpliterator spliterator =
         new Spliterators.AbstractLongSpliterator(
             count,
             Spliterator.IMMUTABLE
@@ -215,8 +214,9 @@
             }
             return false;
           }
-        },
-        false);
+        };
+
+    return StreamSupport.longStream(spliterator, false);
   }
 
   /**
@@ -264,6 +264,7 @@
 
   void forEachOrdered(LongConsumer action);
 
+  @Override
   PrimitiveIterator.OfLong iterator();
 
   LongStream limit(long maxSize);
@@ -282,6 +283,7 @@
 
   boolean noneMatch(LongPredicate predicate);
 
+  @Override
   LongStream parallel();
 
   LongStream peek(LongConsumer action);
@@ -290,12 +292,14 @@
 
   long reduce(long identity, LongBinaryOperator op);
 
+  @Override
   LongStream sequential();
 
   LongStream skip(long n);
 
   LongStream sorted();
 
+  @Override
   Spliterator.OfLong spliterator();
 
   long sum();
@@ -717,7 +721,7 @@
 
     @Override
     public boolean tryAdvance(LongConsumer action) {
-      if (limit <= position) {
+      if (position >= limit) {
         return false;
       }
       boolean result = original.tryAdvance(action);
@@ -799,24 +803,20 @@
 
     @Override
     public OptionalLong min() {
-      terminate();
-      final ValueConsumer holder = new ValueConsumer();
-      if (spliterator.tryAdvance((long value) -> holder.value = value)) {
-        spliterator.forEachRemaining((long value) -> holder.value = Math.min(holder.value, value));
-        return OptionalLong.of(holder.value);
+      LongSummaryStatistics stats = summaryStatistics();
+      if (stats.getCount() == 0) {
+        return OptionalLong.empty();
       }
-      return OptionalLong.empty();
+      return OptionalLong.of(stats.getMin());
     }
 
     @Override
     public OptionalLong max() {
-      terminate();
-      final ValueConsumer holder = new ValueConsumer();
-      if (spliterator.tryAdvance((long value) -> holder.value = value)) {
-        spliterator.forEachRemaining((long value) -> holder.value = Math.max(holder.value, value));
-        return OptionalLong.of(holder.value);
+      LongSummaryStatistics stats = summaryStatistics();
+      if (stats.getCount() == 0) {
+        return OptionalLong.empty();
       }
-      return OptionalLong.empty();
+      return OptionalLong.of(stats.getMax());
     }
 
     @Override
@@ -905,7 +905,7 @@
     @Override
     public <U> Stream<U> mapToObj(LongFunction<? extends U> mapper) {
       throwIfTerminated();
-      return new Stream.StreamSource(this, new MapToObjSpliterator<U>(mapper, spliterator));
+      return new Stream.StreamSource<U>(this, new MapToObjSpliterator<U>(mapper, spliterator));
     }
 
     @Override
@@ -926,8 +926,8 @@
       throwIfTerminated();
       final Spliterator<? extends LongStream> spliteratorOfStreams =
           new MapToObjSpliterator<>(mapper, spliterator);
-      return new LongStreamSource(
-          this,
+
+      AbstractLongSpliterator flatMapSpliterator =
           new Spliterators.AbstractLongSpliterator(Long.MAX_VALUE, 0) {
             LongStream nextStream;
             Spliterator.OfLong next;
@@ -963,7 +963,9 @@
               }
               return true;
             }
-          });
+          };
+
+      return new LongStreamSource(this, flatMapSpliterator);
     }
 
     @Override
@@ -976,8 +978,8 @@
     @Override
     public LongStream sorted() {
       throwIfTerminated();
-      return new LongStreamSource(
-          this,
+
+      AbstractLongSpliterator sortedSpliterator =
           new Spliterators.AbstractLongSpliterator(
               spliterator.estimateSize(), spliterator.characteristics() | Spliterator.SORTED) {
             Spliterator.OfLong ordered = null;
@@ -997,22 +999,26 @@
               }
               return ordered.tryAdvance(action);
             }
-          });
+          };
+
+      return new LongStreamSource(this, sortedSpliterator);
     }
 
     @Override
     public LongStream peek(LongConsumer action) {
       checkNotNull(action);
       throwIfTerminated();
-      return new LongStreamSource(
-          this,
+
+      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
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 dfd6d8a..a313f4a 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
@@ -16,7 +16,6 @@
 
 package java.util.stream;
 
-import static javaemul.internal.InternalPreconditions.checkCriticalState;
 import static javaemul.internal.InternalPreconditions.checkNotNull;
 import static javaemul.internal.InternalPreconditions.checkState;
 
@@ -30,6 +29,10 @@
 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;
@@ -78,7 +81,7 @@
       @Override
       @SuppressWarnings("unchecked")
       public Stream<T> build() {
-        checkCriticalState(items != null, "Builder already built");
+        checkState(items != null, "Builder already built");
         Stream<T> stream = (Stream<T>) Arrays.stream(items);
         items = null;
         return stream;
@@ -96,44 +99,42 @@
     // streams
     Spliterator<? extends Stream<? extends T>> spliteratorOfStreams =
         Arrays.asList(a, b).spliterator();
-    Stream<T> result =
-        new StreamSource<T>(
-            null,
-            new Spliterators.AbstractSpliterator<T>(Long.MAX_VALUE, 0) {
-              Stream<? extends T> nextStream;
-              Spliterator<? extends T> next;
 
-              @Override
-              public boolean tryAdvance(Consumer<? super T> 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 = null;
-                    // failed, null it out so we can find another
-                    next = null;
-                  }
-                }
+    AbstractSpliterator<T> spliterator =
+        new Spliterators.AbstractSpliterator<T>(Long.MAX_VALUE, 0) {
+          Spliterator<? extends T> next;
+
+          @Override
+          public boolean tryAdvance(Consumer<? super T> 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 {
+                // 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) {
+                      next = n.spliterator();
+                    }
+                  })) {
                 return false;
               }
+            }
+            return true;
+          }
+        };
 
-              private boolean advanceToNextSpliterator() {
-                while (next == null) {
-                  if (!spliteratorOfStreams.tryAdvance(
-                      n -> {
-                        if (n != null) {
-                          nextStream = n;
-                          next = n.spliterator();
-                        }
-                      })) {
-                    return false;
-                  }
-                }
-                return true;
-              }
-            });
+    Stream<T> result = new StreamSource<T>(null, spliterator);
 
     result.onClose(a::close);
     result.onClose(b::close);
@@ -146,7 +147,7 @@
   }
 
   static <T> Stream<T> generate(Supplier<T> s) {
-    return StreamSupport.stream(
+    AbstractSpliterator<T> spliterator =
         new Spliterators.AbstractSpliterator<T>(
             Long.MAX_VALUE, Spliterator.IMMUTABLE | Spliterator.ORDERED) {
           @Override
@@ -154,12 +155,12 @@
             action.accept(s.get());
             return true;
           }
-        },
-        false);
+        };
+    return StreamSupport.stream(spliterator, false);
   }
 
   static <T> Stream<T> iterate(T seed, UnaryOperator<T> f) {
-    return StreamSupport.stream(
+    AbstractSpliterator<T> spliterator =
         new Spliterators.AbstractSpliterator<T>(
             Long.MAX_VALUE, Spliterator.IMMUTABLE | Spliterator.ORDERED) {
           private T next = seed;
@@ -170,8 +171,8 @@
             next = f.apply(next);
             return true;
           }
-        },
-        false);
+        };
+    return StreamSupport.stream(spliterator, false);
   }
 
   static <T> Stream<T> of(T t) {
@@ -702,7 +703,7 @@
 
     @Override
     public boolean tryAdvance(Consumer<? super T> action) {
-      if (limit <= position) {
+      if (position >= limit) {
         return false;
       }
       boolean result = original.tryAdvance(action);
@@ -901,8 +902,8 @@
       throwIfTerminated();
       final Spliterator<? extends Stream<? extends R>> spliteratorOfStreams =
           new MapToObjSpliterator<>(mapper, spliterator);
-      return new StreamSource<R>(
-          this,
+
+      AbstractSpliterator<R> flatMapSpliterator =
           new Spliterators.AbstractSpliterator<R>(Long.MAX_VALUE, 0) {
             Stream<? extends R> nextStream;
             Spliterator<? extends R> next;
@@ -938,7 +939,9 @@
               }
               return true;
             }
-          });
+          };
+
+      return new StreamSource<R>(this, flatMapSpliterator);
     }
 
     @Override
@@ -946,8 +949,8 @@
       throwIfTerminated();
       final Spliterator<? extends IntStream> spliteratorOfStreams =
           new MapToObjSpliterator<>(mapper, spliterator);
-      return new IntStream.IntStreamSource(
-          this,
+
+      AbstractIntSpliterator flatMapSpliterator =
           new Spliterators.AbstractIntSpliterator(Long.MAX_VALUE, 0) {
             IntStream nextStream;
             Spliterator.OfInt next;
@@ -983,7 +986,9 @@
               }
               return true;
             }
-          });
+          };
+
+      return new IntStream.IntStreamSource(this, flatMapSpliterator);
     }
 
     @Override
@@ -991,8 +996,8 @@
       throwIfTerminated();
       final Spliterator<? extends LongStream> spliteratorOfStreams =
           new MapToObjSpliterator<>(mapper, spliterator);
-      return new LongStream.LongStreamSource(
-          this,
+
+      AbstractLongSpliterator flatMapSpliterator =
           new Spliterators.AbstractLongSpliterator(Long.MAX_VALUE, 0) {
             LongStream nextStream;
             Spliterator.OfLong next;
@@ -1028,7 +1033,9 @@
               }
               return true;
             }
-          });
+          };
+
+      return new LongStream.LongStreamSource(this, flatMapSpliterator);
     }
 
     @Override
@@ -1036,8 +1043,8 @@
       throwIfTerminated();
       final Spliterator<? extends DoubleStream> spliteratorOfStreams =
           new MapToObjSpliterator<>(mapper, spliterator);
-      return new DoubleStream.DoubleStreamSource(
-          this,
+
+      AbstractDoubleSpliterator flatMapSpliterator =
           new Spliterators.AbstractDoubleSpliterator(Long.MAX_VALUE, 0) {
             DoubleStream nextStream;
             Spliterator.OfDouble next;
@@ -1073,7 +1080,9 @@
               }
               return true;
             }
-          });
+          };
+
+      return new DoubleStream.DoubleStreamSource(this, flatMapSpliterator);
     }
 
     @Override
@@ -1093,8 +1102,8 @@
     @Override
     public Stream<T> sorted(final Comparator<? super T> comparator) {
       throwIfTerminated();
-      return new StreamSource<>(
-          this,
+
+      AbstractSpliterator<T> sortedSpliterator =
           new Spliterators.AbstractSpliterator<T>(
               spliterator.estimateSize(), spliterator.characteristics() | Spliterator.SORTED) {
             Spliterator<T> ordered = null;
@@ -1114,15 +1123,17 @@
               }
               return ordered.tryAdvance(action);
             }
-          });
+          };
+
+      return new StreamSource<>(this, sortedSpliterator);
     }
 
     @Override
     public Stream<T> peek(final Consumer<? super T> action) {
       checkNotNull(action);
       throwIfTerminated();
-      return new StreamSource<>(
-          this,
+
+      AbstractSpliterator<T> peekSpliterator =
           new Spliterators.AbstractSpliterator<T>(
               spliterator.estimateSize(), spliterator.characteristics()) {
             @Override
@@ -1133,7 +1144,9 @@
                     innerAction.accept(item);
                   });
             }
-          });
+          };
+
+      return new StreamSource<>(this, peekSpliterator);
     }
 
     @Override