Emulate java.util.Spliterator

This implementation does not perform split operations yet.

Change-Id: I40c80f2104c26a21694b1132904acd6971496b1c
diff --git a/user/super/com/google/gwt/emul/java/lang/Iterable.java b/user/super/com/google/gwt/emul/java/lang/Iterable.java
index b68cb6b..cec56d3 100644
--- a/user/super/com/google/gwt/emul/java/lang/Iterable.java
+++ b/user/super/com/google/gwt/emul/java/lang/Iterable.java
@@ -18,6 +18,8 @@
 import static javaemul.internal.InternalPreconditions.checkNotNull;
 
 import java.util.Iterator;
+import java.util.Spliterator;
+import java.util.Spliterators;
 import java.util.function.Consumer;
 
 /**
@@ -37,4 +39,8 @@
       action.accept(t);
     }
   }
+
+  default Spliterator<T> spliterator() {
+    return Spliterators.spliteratorUnknownSize(iterator(), 0);
+  }
 }
diff --git a/user/super/com/google/gwt/emul/java/util/Arrays.java b/user/super/com/google/gwt/emul/java/util/Arrays.java
index 9ad9fc7..374a557 100644
--- a/user/super/com/google/gwt/emul/java/util/Arrays.java
+++ b/user/super/com/google/gwt/emul/java/util/Arrays.java
@@ -1043,6 +1043,43 @@
     mergeSort(x, fromIndex, toIndex, c);
   }
 
+  public static Spliterator.OfDouble spliterator(double[] array) {
+    return Spliterators.spliterator(array, Spliterator.IMMUTABLE | Spliterator.ORDERED);
+  }
+
+  public static Spliterator.OfDouble spliterator(double[] array,
+      int startInclusive, int endExclusive) {
+    return Spliterators.spliterator(array, startInclusive, endExclusive,
+        Spliterator.IMMUTABLE | Spliterator.ORDERED);
+  }
+
+  public static Spliterator.OfInt spliterator(int[] array) {
+    return Spliterators.spliterator(array, Spliterator.IMMUTABLE | Spliterator.ORDERED);
+  }
+
+  public static Spliterator.OfInt spliterator(int[] array, int startInclusive, int endExclusive) {
+    return Spliterators.spliterator(array, startInclusive, endExclusive,
+        Spliterator.IMMUTABLE | Spliterator.ORDERED);
+  }
+
+  public static Spliterator.OfLong spliterator(long[] array) {
+    return Spliterators.spliterator(array, Spliterator.IMMUTABLE | Spliterator.ORDERED);
+  }
+
+  public static Spliterator.OfLong spliterator(long[] array, int startInclusive, int endExclusive) {
+    return Spliterators.spliterator(array, startInclusive, endExclusive,
+        Spliterator.IMMUTABLE | Spliterator.ORDERED);
+  }
+
+  public static <T> Spliterator<T> spliterator(T[] array) {
+    return Spliterators.spliterator(array, Spliterator.IMMUTABLE | Spliterator.ORDERED);
+  }
+
+  public static <T> Spliterator<T> spliterator(T[] array, int startInclusive, int endExclusive) {
+    return Spliterators.spliterator(array, startInclusive, endExclusive,
+        Spliterator.IMMUTABLE | Spliterator.ORDERED);
+  }
+
   public static String toString(boolean[] a) {
     if (a == null) {
       return "null";
diff --git a/user/super/com/google/gwt/emul/java/util/Collection.java b/user/super/com/google/gwt/emul/java/util/Collection.java
index 0204f64..45b4b37 100644
--- a/user/super/com/google/gwt/emul/java/util/Collection.java
+++ b/user/super/com/google/gwt/emul/java/util/Collection.java
@@ -53,6 +53,11 @@
 
   int size();
 
+  @Override
+  default Spliterator<E> spliterator() {
+    return Spliterators.spliterator(this, 0);
+  }
+
   Object[] toArray();
 
   <T> T[] toArray(T[] a);
diff --git a/user/super/com/google/gwt/emul/java/util/List.java b/user/super/com/google/gwt/emul/java/util/List.java
index a9212bf..b6bd629 100644
--- a/user/super/com/google/gwt/emul/java/util/List.java
+++ b/user/super/com/google/gwt/emul/java/util/List.java
@@ -80,6 +80,11 @@
   @Override
   int size();
 
+  @Override
+  default Spliterator<E> spliterator() {
+    return Spliterators.spliterator(this, Spliterator.ORDERED);
+  }
+
   List<E> subList(int fromIndex, int toIndex);
 
   @Override
diff --git a/user/super/com/google/gwt/emul/java/util/PriorityQueue.java b/user/super/com/google/gwt/emul/java/util/PriorityQueue.java
index 9a595c1..7fadc73 100644
--- a/user/super/com/google/gwt/emul/java/util/PriorityQueue.java
+++ b/user/super/com/google/gwt/emul/java/util/PriorityQueue.java
@@ -194,6 +194,11 @@
   }
 
   @Override
+  public Spliterator<E> spliterator() {
+    return Spliterators.spliterator(this, Spliterator.NONNULL);
+  }
+
+  @Override
   public Object[] toArray() {
     return heap.toArray();
   }
diff --git a/user/super/com/google/gwt/emul/java/util/Set.java b/user/super/com/google/gwt/emul/java/util/Set.java
index 8c20796..07e6c18 100644
--- a/user/super/com/google/gwt/emul/java/util/Set.java
+++ b/user/super/com/google/gwt/emul/java/util/Set.java
@@ -63,6 +63,11 @@
   int size();
 
   @Override
+  default Spliterator<E> spliterator() {
+    return Spliterators.spliterator(this, Spliterator.DISTINCT);
+  }
+
+  @Override
   Object[] toArray();
 
   @Override
diff --git a/user/super/com/google/gwt/emul/java/util/SortedSet.java b/user/super/com/google/gwt/emul/java/util/SortedSet.java
index bc7a064..95e8fc8 100644
--- a/user/super/com/google/gwt/emul/java/util/SortedSet.java
+++ b/user/super/com/google/gwt/emul/java/util/SortedSet.java
@@ -35,4 +35,16 @@
   SortedSet<E> subSet(E fromElement, E toElement);
 
   SortedSet<E> tailSet(E fromElement);
+
+  @Override
+  default Spliterator<E> spliterator() {
+    return new Spliterators.IteratorSpliterator<E>(this,
+        Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SORTED) {
+
+      @Override
+      public Comparator<? super E> getComparator() {
+        return SortedSet.this.comparator();
+      }
+    };
+  }
 }
diff --git a/user/super/com/google/gwt/emul/java/util/Spliterator.java b/user/super/com/google/gwt/emul/java/util/Spliterator.java
new file mode 100644
index 0000000..365efd2
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/util/Spliterator.java
@@ -0,0 +1,173 @@
+/*
+ * 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;
+
+import static javaemul.internal.InternalPreconditions.checkCriticalNotNull;
+import static javaemul.internal.InternalPreconditions.checkNotNull;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+/**
+ * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.html">
+ * the official Java API doc</a> for details.
+ *
+ * @param <T> the type of elements returned by Spliterator.
+ */
+public interface Spliterator<T> {
+
+  int DISTINCT = 0x00000001;
+
+  int ORDERED = 0x00000010;
+
+  int NONNULL = 0x00000100;
+
+  int CONCURRENT = 0x00001000;
+
+  int SORTED = 0x00000004;
+
+  int SIZED = 0x00000040;
+
+  int IMMUTABLE = 0x00000400;
+
+  int SUBSIZED = 0x00004000;
+
+  int characteristics();
+
+  long estimateSize();
+
+  default void forEachRemaining(Consumer<? super T> consumer) {
+    checkNotNull(consumer);
+    while (tryAdvance(consumer)) { }
+  }
+
+  default Comparator<? super T> getComparator() {
+    throw new IllegalStateException();
+  }
+
+  default long getExactSizeIfKnown() {
+    return hasCharacteristics(SIZED) ? estimateSize() : -1L;
+  }
+
+  default boolean hasCharacteristics(int characteristics) {
+    return (characteristics() & characteristics) != 0;
+  }
+
+  boolean tryAdvance(Consumer<? super T> consumer);
+
+  Spliterator<T> trySplit();
+
+  /**
+   * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.OfPrimitive.html">
+   * the official Java API doc</a> for details.
+   *
+   * @param <T> the type of elements returned by this Spliterator.
+   * @param <C> the type of primitive Consumer.
+   * @param <S> the type of primitive Spliterator.
+   */
+  interface OfPrimitive<T, C, S extends OfPrimitive<T, C, S>> extends Spliterator<T> {
+
+    boolean tryAdvance(C consumer);
+
+    @Override
+    S trySplit();
+
+    default void forEachRemaining(C consumer) {
+      checkNotNull(consumer);
+      while (tryAdvance(consumer)) { }
+    }
+  }
+
+  /**
+   * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.OfDouble.html">
+   * the official Java API doc</a> for details.
+   */
+  interface OfDouble extends OfPrimitive<Double, DoubleConsumer, OfDouble> {
+    @Override
+    default boolean tryAdvance(Consumer<? super Double> consumer) {
+      if (consumer instanceof DoubleConsumer) {
+        return tryAdvance((DoubleConsumer) consumer);
+      } else {
+        checkCriticalNotNull(consumer);
+        return tryAdvance((DoubleConsumer) consumer::accept);
+      }
+    }
+
+    @Override
+    default void forEachRemaining(Consumer<? super Double> consumer) {
+      if (consumer instanceof DoubleConsumer) {
+        forEachRemaining((DoubleConsumer) consumer);
+      } else {
+        checkCriticalNotNull(consumer);
+        forEachRemaining((DoubleConsumer) consumer::accept);
+      }
+    }
+  }
+
+  /**
+   * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.OfInt.html">
+   * the official Java API doc</a> for details.
+   */
+  interface OfInt extends OfPrimitive<Integer, IntConsumer, OfInt> {
+    @Override
+    default boolean tryAdvance(Consumer<? super Integer> consumer) {
+      if (consumer instanceof IntConsumer) {
+        return tryAdvance((IntConsumer) consumer);
+      } else {
+        checkCriticalNotNull(consumer);
+        return tryAdvance((IntConsumer) consumer::accept);
+      }
+    }
+
+    @Override
+    default void forEachRemaining(Consumer<? super Integer> consumer) {
+      if (consumer instanceof IntConsumer) {
+        forEachRemaining((IntConsumer) consumer);
+      } else {
+        checkCriticalNotNull(consumer);
+        forEachRemaining((IntConsumer) consumer::accept);
+      }
+    }
+  }
+
+  /**
+   * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.OfLong.html">
+   * the official Java API doc</a> for details.
+   */
+  interface OfLong extends OfPrimitive<Long, LongConsumer, OfLong> {
+    @Override
+    default boolean tryAdvance(Consumer<? super Long> consumer) {
+      if (consumer instanceof LongConsumer) {
+        return tryAdvance((LongConsumer) consumer);
+      } else {
+        checkCriticalNotNull(consumer);
+        return tryAdvance((LongConsumer) consumer::accept);
+      }
+    }
+
+    @Override
+    default void forEachRemaining(Consumer<? super Long> consumer) {
+      if (consumer instanceof LongConsumer) {
+        forEachRemaining((LongConsumer) consumer);
+      } else {
+        checkCriticalNotNull(consumer);
+        forEachRemaining((LongConsumer) consumer::accept);
+      }
+    }
+  }
+}
diff --git a/user/super/com/google/gwt/emul/java/util/Spliterators.java b/user/super/com/google/gwt/emul/java/util/Spliterators.java
new file mode 100644
index 0000000..7e44465
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/util/Spliterators.java
@@ -0,0 +1,736 @@
+/*
+ * 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;
+
+import static javaemul.internal.InternalPreconditions.checkCriticalElement;
+import static javaemul.internal.InternalPreconditions.checkCriticalPositionIndexes;
+import static javaemul.internal.InternalPreconditions.checkCriticalState;
+import static javaemul.internal.InternalPreconditions.checkNotNull;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+/**
+ * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Spliterators.html">
+ * the official Java API doc</a> for details.
+ *
+ * Since it's hard to implement parallel algorithms in the browser environment
+ * and to keep code simple, implementation does not provide splitting.
+ */
+public final class Spliterators {
+
+  private static class BaseSpliterator<T, S extends Spliterator<T>> {
+    private final int characteristics;
+    private long sizeEstimate;
+
+    BaseSpliterator(long size, int characteristics) {
+      this.sizeEstimate = size;
+      this.characteristics = (characteristics & Spliterator.SIZED) != 0 ?
+          characteristics | Spliterator.SUBSIZED : characteristics;
+    }
+
+    public int characteristics() {
+      return characteristics;
+    }
+
+    public long estimateSize() {
+      return sizeEstimate;
+    }
+
+    public S trySplit() {
+      // see javadoc for java.util.Spliterator
+      return null;
+    }
+  }
+
+  /**
+   * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Spliterators.AbstractSpliterator.html">
+   * the official Java API doc</a> for details.
+   */
+  public abstract static class AbstractSpliterator<T>
+      extends BaseSpliterator<T, Spliterator<T>> implements Spliterator<T> {
+
+    protected AbstractSpliterator(long size, int characteristics) {
+      super(size, characteristics);
+    }
+  }
+
+  /**
+   * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Spliterators.AbstractDoubleSpliterator.html">
+   * the official Java API doc</a> for details.
+   */
+  public abstract static class AbstractDoubleSpliterator
+      extends BaseSpliterator<Double, Spliterator.OfDouble> implements Spliterator.OfDouble {
+
+    protected AbstractDoubleSpliterator(long size, int characteristics) {
+      super(size, characteristics);
+    }
+  }
+
+  /**
+   * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Spliterators.AbstractIntSpliterator.html">
+   * the official Java API doc</a> for details.
+   */
+  public abstract static class AbstractIntSpliterator
+      extends BaseSpliterator<Integer, Spliterator.OfInt> implements Spliterator.OfInt {
+
+    protected AbstractIntSpliterator(long size, int characteristics) {
+      super(size, characteristics);
+    }
+  }
+
+  /**
+   * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Spliterators.AbstractLongSpliterator.html">
+   * the official Java API doc</a> for details.
+   */
+  public abstract static class AbstractLongSpliterator
+      extends BaseSpliterator<Long, Spliterator.OfLong> implements Spliterator.OfLong {
+
+    protected AbstractLongSpliterator(long size, int characteristics) {
+      super(size, characteristics);
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  public static <T> Spliterator<T> emptySpliterator() {
+    return (Spliterator<T>) EmptySpliterator.OF_REF;
+  }
+
+  public static Spliterator.OfDouble emptyDoubleSpliterator() {
+    return EmptySpliterator.OF_DOUBLE;
+  }
+
+  public static Spliterator.OfInt emptyIntSpliterator() {
+    return EmptySpliterator.OF_INT;
+  }
+
+  public static Spliterator.OfLong emptyLongSpliterator() {
+    return EmptySpliterator.OF_LONG;
+  }
+
+  public static <T> Spliterator<T> spliterator(Object[] array, int characteristics) {
+    return new ArraySpliterator<>(array, characteristics);
+  }
+
+  public static <T> Spliterator<T> spliterator(Object[] array, int fromIndex, int toIndex,
+                                               int characteristics) {
+    return new ArraySpliterator<>(array, fromIndex, toIndex, characteristics);
+  }
+
+  public static Spliterator.OfInt spliterator(int[] array, int characteristics) {
+    return new IntArraySpliterator(array, characteristics);
+  }
+
+  public static Spliterator.OfInt spliterator(int[] array, int fromIndex, int toIndex,
+                                              int characteristics) {
+    return new IntArraySpliterator(array, fromIndex, toIndex, characteristics);
+  }
+
+  public static Spliterator.OfLong spliterator(long[] array, int characteristics) {
+    return new LongArraySpliterator(array, characteristics);
+  }
+
+  public static Spliterator.OfLong spliterator(long[] array, int fromIndex, int toIndex,
+                                               int characteristics) {
+    return new LongArraySpliterator(array, fromIndex, toIndex, characteristics);
+  }
+
+  public static Spliterator.OfDouble spliterator(double[] array, int characteristics) {
+    return new DoubleArraySpliterator(array, characteristics);
+  }
+
+  public static Spliterator.OfDouble spliterator(double[] array, int fromIndex, int toIndex,
+                                                 int characteristics) {
+    return new DoubleArraySpliterator(array, fromIndex, toIndex, characteristics);
+  }
+
+  public static <T> Spliterator<T> spliterator(Collection<? extends T> c, int characteristics) {
+    return new IteratorSpliterator<>(c, characteristics);
+  }
+
+  public static <T> Spliterator<T> spliterator(Iterator<? extends T> it, long size,
+                                               int characteristics) {
+    return new IteratorSpliterator<>(it, size, characteristics);
+  }
+
+  public static <T> Spliterator<T> spliteratorUnknownSize(Iterator<? extends T> it,
+                                                          int characteristics) {
+    return new IteratorSpliterator<>(it, characteristics);
+  }
+
+  public static Spliterator.OfInt spliterator(PrimitiveIterator.OfInt it, long size,
+                                              int characteristics) {
+    return new IntIteratorSpliterator(it, size, characteristics);
+  }
+
+  public static Spliterator.OfInt spliteratorUnknownSize(PrimitiveIterator.OfInt it,
+                                                         int characteristics) {
+    return new IntIteratorSpliterator(it, characteristics);
+  }
+
+  public static Spliterator.OfLong spliterator(PrimitiveIterator.OfLong it, long size,
+                                               int characteristics) {
+    return new LongIteratorSpliterator(it, size, characteristics);
+  }
+
+  public static Spliterator.OfLong spliteratorUnknownSize(PrimitiveIterator.OfLong it,
+                                                          int characteristics) {
+    return new LongIteratorSpliterator(it, characteristics);
+  }
+
+  public static Spliterator.OfDouble spliterator(PrimitiveIterator.OfDouble it, long size,
+                                                 int characteristics) {
+    return new DoubleIteratorSpliterator(it, size, characteristics);
+  }
+
+  public static Spliterator.OfDouble spliteratorUnknownSize(PrimitiveIterator.OfDouble it,
+                                                            int characteristics) {
+    return new DoubleIteratorSpliterator(it, characteristics);
+  }
+
+  public static <T> Iterator<T> iterator(Spliterator<? extends T> spliterator) {
+    return new ConsumerIterator<>(spliterator);
+  }
+
+  public static PrimitiveIterator.OfDouble iterator(Spliterator.OfDouble spliterator) {
+    return new DoubleConsumerIterator(spliterator);
+  }
+
+  public static PrimitiveIterator.OfInt iterator(Spliterator.OfInt spliterator) {
+    return new IntConsumerIterator(spliterator);
+  }
+
+  public static PrimitiveIterator.OfLong iterator(Spliterator.OfLong spliterator) {
+    return new LongConsumerIterator(spliterator);
+  }
+
+  private abstract static class EmptySpliterator<T, S extends Spliterator<T>, C> {
+
+    static final Spliterator<Object> OF_REF = new EmptySpliterator.OfRef<>();
+    static final Spliterator.OfDouble OF_DOUBLE = new EmptySpliterator.OfDouble();
+    static final Spliterator.OfInt OF_INT = new EmptySpliterator.OfInt();
+    static final Spliterator.OfLong OF_LONG = new EmptySpliterator.OfLong();
+
+    public int characteristics() {
+      return Spliterator.SIZED | Spliterator.SUBSIZED;
+    }
+
+    public long estimateSize() {
+      return 0;
+    }
+
+    public void forEachRemaining(C consumer) {
+      checkNotNull(consumer);
+    }
+
+    public boolean tryAdvance(C consumer) {
+      checkNotNull(consumer);
+      return false;
+    }
+
+    public S trySplit() {
+      return null;
+    }
+
+    private static final class OfRef<T>
+        extends EmptySpliterator<T, Spliterator<T>, Consumer<? super T>>
+        implements Spliterator<T> {
+
+      OfRef() { }
+    }
+
+    private static final class OfDouble
+        extends EmptySpliterator<Double, Spliterator.OfDouble, DoubleConsumer>
+        implements Spliterator.OfDouble {
+
+      OfDouble() { }
+    }
+
+    private static final class OfInt
+        extends EmptySpliterator<Integer, Spliterator.OfInt, IntConsumer>
+        implements Spliterator.OfInt {
+
+      OfInt() { }
+    }
+
+    private static final class OfLong
+        extends EmptySpliterator<Long, Spliterator.OfLong, LongConsumer>
+        implements Spliterator.OfLong {
+
+      OfLong() { }
+    }
+  }
+
+  private static final class ConsumerIterator<T> implements Consumer<T>, Iterator<T> {
+    private final Spliterator<? extends T> spliterator;
+    private T nextElement;
+    private boolean hasElement = false;
+
+    ConsumerIterator(Spliterator<? extends T> spliterator) {
+      this.spliterator = checkNotNull(spliterator);
+    }
+
+    @Override
+    public void accept(T element) {
+      nextElement = element;
+    }
+
+    @Override
+    public boolean hasNext() {
+      if (!hasElement) {
+        hasElement = spliterator.tryAdvance(this);
+      }
+      return hasElement;
+    }
+
+    @Override
+    public T next() {
+      checkCriticalElement(hasNext());
+      hasElement = false;
+      T element = nextElement;
+      nextElement = null;
+      return element;
+    }
+  }
+
+  private static final class DoubleConsumerIterator
+      implements DoubleConsumer, PrimitiveIterator.OfDouble {
+
+    private final Spliterator.OfDouble spliterator;
+    private double nextElement;
+    private boolean hasElement = false;
+
+    DoubleConsumerIterator(Spliterator.OfDouble spliterator) {
+      this.spliterator = checkNotNull(spliterator);
+    }
+
+    @Override
+    public void accept(double d) {
+      nextElement = d;
+    }
+
+    @Override
+    public boolean hasNext() {
+      if (!hasElement) {
+        hasElement = spliterator.tryAdvance(this);
+      }
+      return hasElement;
+    }
+
+    @Override
+    public double nextDouble() {
+      checkCriticalElement(hasNext());
+      hasElement = false;
+      return nextElement;
+    }
+  }
+
+  private static final class IntConsumerIterator
+      implements IntConsumer, PrimitiveIterator.OfInt {
+
+    private final Spliterator.OfInt spliterator;
+    private int nextElement;
+    private boolean hasElement = false;
+
+    IntConsumerIterator(Spliterator.OfInt spliterator) {
+      this.spliterator = checkNotNull(spliterator);
+    }
+
+    @Override
+    public void accept(int i) {
+      nextElement = i;
+    }
+
+    @Override
+    public boolean hasNext() {
+      if (!hasElement) {
+        hasElement = spliterator.tryAdvance(this);
+      }
+      return hasElement;
+    }
+
+    @Override
+    public int nextInt() {
+      checkCriticalElement(hasNext());
+      hasElement = false;
+      return nextElement;
+    }
+  }
+
+  private static final class LongConsumerIterator
+      implements LongConsumer, PrimitiveIterator.OfLong {
+
+    private final Spliterator.OfLong spliterator;
+    private long nextElement;
+    private boolean hasElement = false;
+
+    LongConsumerIterator(Spliterator.OfLong spliterator) {
+      this.spliterator = checkNotNull(spliterator);
+    }
+
+    @Override
+    public void accept(long l) {
+      nextElement = l;
+    }
+
+    @Override
+    public boolean hasNext() {
+      if (!hasElement) {
+        hasElement = spliterator.tryAdvance(this);
+      }
+      return hasElement;
+    }
+
+    @Override
+    public long nextLong() {
+      checkCriticalElement(hasNext());
+      hasElement = false;
+      return nextElement;
+    }
+  }
+
+  static class IteratorSpliterator<T> implements Spliterator<T> {
+    private Collection<? extends T> collection;
+    private Iterator<? extends T> it;
+    private final int characteristics;
+    private long estimateSize;
+
+    IteratorSpliterator(Collection<? extends T> collection, int characteristics) {
+      this.collection = checkNotNull(collection);
+      this.characteristics = sizeKnownIteratorSpliteratorCharacteristics(characteristics);
+    }
+
+    IteratorSpliterator(Iterator<? extends T> it, long size, int characteristics) {
+      this.it = checkNotNull(it);
+      this.characteristics = sizeKnownIteratorSpliteratorCharacteristics(characteristics);
+      this.estimateSize = size;
+    }
+
+    IteratorSpliterator(Iterator<? extends T> it, int characteristics) {
+      this.it = checkNotNull(it);
+      this.characteristics = sizeUnknownSpliteratorCharacteristics(characteristics);
+      this.estimateSize = Long.MAX_VALUE;
+    }
+
+    @Override
+    public int characteristics() {
+      return characteristics;
+    }
+
+    @Override
+    public long estimateSize() {
+      initIterator();
+      return estimateSize;
+    }
+
+    @Override
+    public void forEachRemaining(Consumer<? super T> consumer) {
+      initIterator();
+      it.forEachRemaining(consumer);
+    }
+
+    @Override
+    public Comparator<? super T> getComparator() {
+      checkSorted(characteristics);
+      return null;
+    }
+
+    @Override
+    public boolean tryAdvance(Consumer<? super T> consumer) {
+      checkNotNull(consumer);
+      initIterator();
+      if (it.hasNext()) {
+        consumer.accept(it.next());
+        return true;
+      }
+      return false;
+    }
+
+    @Override
+    public Spliterator<T> trySplit() {
+      // see javadoc for java.util.Spliterator
+      return null;
+    }
+
+    private void initIterator() {
+      if (it == null) {
+        it = collection.iterator();
+        estimateSize = (long) collection.size();
+      }
+    }
+  }
+
+  private static final class DoubleIteratorSpliterator extends AbstractDoubleSpliterator {
+    private final PrimitiveIterator.OfDouble it;
+
+    DoubleIteratorSpliterator(PrimitiveIterator.OfDouble it, long size, int characteristics) {
+      super(size, sizeKnownIteratorSpliteratorCharacteristics(characteristics));
+      this.it = checkNotNull(it);
+    }
+
+    DoubleIteratorSpliterator(PrimitiveIterator.OfDouble it, int characteristics) {
+      super(Long.MAX_VALUE, sizeUnknownSpliteratorCharacteristics(characteristics));
+      this.it = checkNotNull(it);
+    }
+
+    @Override
+    public void forEachRemaining(DoubleConsumer consumer) {
+      it.forEachRemaining(consumer);
+    }
+
+    @Override
+    public Comparator<? super Double> getComparator() {
+      checkSorted(characteristics());
+      return null;
+    }
+
+    @Override
+    public boolean tryAdvance(DoubleConsumer consumer) {
+      checkNotNull(consumer);
+      if (it.hasNext()) {
+        consumer.accept(it.nextDouble());
+        return true;
+      }
+      return false;
+    }
+  }
+
+  private static final class IntIteratorSpliterator extends AbstractIntSpliterator {
+    private final PrimitiveIterator.OfInt it;
+
+    IntIteratorSpliterator(PrimitiveIterator.OfInt it, long size, int characteristics) {
+      super(size, sizeKnownIteratorSpliteratorCharacteristics(characteristics));
+      this.it = checkNotNull(it);
+    }
+
+    IntIteratorSpliterator(PrimitiveIterator.OfInt it, int characteristics) {
+      super(Long.MAX_VALUE, sizeUnknownSpliteratorCharacteristics(characteristics));
+      this.it = checkNotNull(it);
+    }
+
+    @Override
+    public void forEachRemaining(IntConsumer consumer) {
+      it.forEachRemaining(consumer);
+    }
+
+    @Override
+    public Comparator<? super Integer> getComparator() {
+      checkSorted(characteristics());
+      return null;
+    }
+
+    @Override
+    public boolean tryAdvance(IntConsumer consumer) {
+      checkNotNull(consumer);
+      if (it.hasNext()) {
+        consumer.accept(it.nextInt());
+        return true;
+      }
+      return false;
+    }
+  }
+
+  private static final class LongIteratorSpliterator extends AbstractLongSpliterator {
+    private final PrimitiveIterator.OfLong it;
+
+    LongIteratorSpliterator(PrimitiveIterator.OfLong it, long size, int characteristics) {
+      super(size, sizeKnownIteratorSpliteratorCharacteristics(characteristics));
+      this.it = checkNotNull(it);
+    }
+
+    LongIteratorSpliterator(PrimitiveIterator.OfLong it, int characteristics) {
+      super(Long.MAX_VALUE, sizeUnknownSpliteratorCharacteristics(characteristics));
+      this.it = checkNotNull(it);
+    }
+
+    @Override
+    public void forEachRemaining(LongConsumer consumer) {
+      it.forEachRemaining(consumer);
+    }
+
+    @Override
+    public Comparator<? super Long> getComparator() {
+      checkSorted(characteristics());
+      return null;
+    }
+
+    @Override
+    public boolean tryAdvance(LongConsumer consumer) {
+      checkNotNull(consumer);
+      if (it.hasNext()) {
+        consumer.accept(it.nextLong());
+        return true;
+      }
+      return false;
+    }
+  }
+
+  private abstract static class BaseArraySpliterator<T, S extends Spliterator<T>, C> {
+    private int index;
+    private final int limit;
+    private final int characteristics;
+
+    BaseArraySpliterator(int from, int limit, int size, int characteristics) {
+      checkCriticalPositionIndexes(from, limit, size);
+      this.index = from;
+      this.limit = limit;
+      this.characteristics = sizeKnownSpliteratorCharacteristics(characteristics);
+    }
+
+    public int characteristics() {
+      return characteristics;
+    }
+
+    public long estimateSize() {
+      return limit - index;
+    }
+
+    public void forEachRemaining(C consumer) {
+      checkNotNull(consumer);
+      while (index < limit) {
+        consume(consumer, index++);
+      }
+    }
+
+    public Comparator<? super T> getComparator() {
+      checkSorted(characteristics);
+      return null;
+    }
+
+    public boolean tryAdvance(C consumer) {
+      checkNotNull(consumer);
+      if (index < limit) {
+        consume(consumer, index++);
+        return true;
+      }
+      return false;
+    }
+
+    public S trySplit() {
+      // see javadoc for java.util.Spliterator
+      return null;
+    }
+
+    protected abstract void consume(C consumer, int index);
+  }
+
+  private static final class ArraySpliterator<T>
+      extends BaseArraySpliterator<T, Spliterator<T>, Consumer<? super T>>
+      implements Spliterator<T> {
+
+    private final Object[] array;
+
+    ArraySpliterator(Object[] array, int characteristics) {
+      this(array, 0, array.length, characteristics);
+    }
+
+    ArraySpliterator(Object[] array, int from, int limit, int characteristics) {
+      super(from, limit, array.length, characteristics);
+      this.array = array;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected void consume(Consumer<? super T> consumer, int index) {
+      consumer.accept((T) array[index]);
+    }
+  }
+
+  private static final class DoubleArraySpliterator
+      extends BaseArraySpliterator<Double, Spliterator.OfDouble, DoubleConsumer>
+      implements Spliterator.OfDouble {
+
+    private final double[] array;
+
+    DoubleArraySpliterator(double[] array, int characteristics) {
+      this(array, 0, array.length, characteristics);
+    }
+
+    DoubleArraySpliterator(double[] array, int from, int limit, int characteristics) {
+      super(from, limit, array.length, characteristics);
+      this.array = array;
+    }
+
+    @Override
+    protected void consume(DoubleConsumer consumer, int index) {
+      consumer.accept(array[index]);
+    }
+  }
+
+  private static final class IntArraySpliterator
+      extends BaseArraySpliterator<Integer, Spliterator.OfInt, IntConsumer>
+      implements Spliterator.OfInt {
+
+    private final int[] array;
+
+    IntArraySpliterator(int[] array, int characteristics) {
+      this(array, 0, array.length, characteristics);
+    }
+
+    IntArraySpliterator(int[] array, int from, int limit, int characteristics) {
+      super(from, limit, array.length, characteristics);
+      this.array = array;
+    }
+
+    @Override
+    protected void consume(IntConsumer consumer, int index) {
+      consumer.accept(array[index]);
+    }
+  }
+
+  private static final class LongArraySpliterator
+      extends BaseArraySpliterator<Long, Spliterator.OfLong, LongConsumer>
+      implements Spliterator.OfLong {
+
+    private final long[] array;
+
+    LongArraySpliterator(long[] array, int characteristics) {
+      this(array, 0, array.length, characteristics);
+    }
+
+    LongArraySpliterator(long[] array, int from, int limit, int characteristics) {
+      super(from, limit, array.length, characteristics);
+      this.array = array;
+    }
+
+    @Override
+    protected void consume(LongConsumer consumer, int index) {
+      consumer.accept(array[index]);
+    }
+  }
+
+  private static void checkSorted(int characteristics) {
+    checkCriticalState((characteristics & Spliterator.SORTED) != 0);
+  }
+
+  private static int sizeKnownSpliteratorCharacteristics(int characteristics) {
+    return characteristics | Spliterator.SIZED | Spliterator.SUBSIZED;
+  }
+
+  private static int sizeKnownIteratorSpliteratorCharacteristics(int characteristics) {
+    return (characteristics & Spliterator.CONCURRENT) == 0 ?
+        sizeKnownSpliteratorCharacteristics(characteristics) : characteristics;
+  }
+
+  private static int sizeUnknownSpliteratorCharacteristics(int characteristics) {
+    return characteristics & ~(Spliterator.SIZED | Spliterator.SUBSIZED);
+  }
+
+  private Spliterators() { }
+
+}
diff --git a/user/test/com/google/gwt/emultest/EmulJava8Suite.java b/user/test/com/google/gwt/emultest/EmulJava8Suite.java
index 7b311b3..0d90d04 100644
--- a/user/test/com/google/gwt/emultest/EmulJava8Suite.java
+++ b/user/test/com/google/gwt/emultest/EmulJava8Suite.java
@@ -24,6 +24,7 @@
 import com.google.gwt.emultest.java8.util.OptionalLongTest;
 import com.google.gwt.emultest.java8.util.OptionalTest;
 import com.google.gwt.emultest.java8.util.PrimitiveIteratorTest;
+import com.google.gwt.emultest.java8.util.SpliteratorsTest;
 import com.google.gwt.emultest.java8.util.StringJoinerTest;
 import com.google.gwt.junit.tools.GWTTestSuite;
 
@@ -44,6 +45,7 @@
     suite.addTestSuite(OptionalLongTest.class);
     suite.addTestSuite(OptionalDoubleTest.class);
     suite.addTestSuite(PrimitiveIteratorTest.class);
+    suite.addTestSuite(SpliteratorsTest.class);
     suite.addTestSuite(StringJoinerTest.class);
     suite.addTestSuite(DoubleSummaryStatisticsTest.class);
     suite.addTestSuite(IntSummaryStatisticsTest.class);
diff --git a/user/test/com/google/gwt/emultest/java8/util/SpliteratorsTest.java b/user/test/com/google/gwt/emultest/java8/util/SpliteratorsTest.java
new file mode 100644
index 0000000..4c5a23b
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java8/util/SpliteratorsTest.java
@@ -0,0 +1,308 @@
+/*
+ * 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 com.google.gwt.emultest.java8.util;
+
+import com.google.gwt.junit.client.GWTTestCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.PrimitiveIterator;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Supplier;
+
+/**
+ * Tests for Spliterators JRE emulation.
+ */
+public class SpliteratorsTest extends GWTTestCase {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.emultest.EmulSuite";
+  }
+
+  public void testEmptySpliterator() {
+    testSpliterator(new Object[0], Spliterators::emptySpliterator, true);
+  }
+
+  public void testEmptyDoubleSpliterator() {
+    testDoubleSpliterator(new double[0], Spliterators::emptyDoubleSpliterator, true);
+  }
+
+  public void testEmptyIntSpliterator() {
+    testIntSpliterator(new int[0], Spliterators::emptyIntSpliterator, true);
+  }
+
+  public void testEmptyLongSpliterator() {
+    testLongSpliterator(new long[0], Spliterators::emptyLongSpliterator, true);
+  }
+
+  public void testSpliterator() {
+    final String[] original = {"1", "2", "3", "4"};
+    testSpliterator(original, () -> Spliterators.spliterator(Arrays.asList(original), 0), true);
+    testSpliterator(original, () -> Spliterators.spliterator(Arrays.asList(original).iterator(), original.length, 0), true);
+    testSpliterator(original, () -> Spliterators.spliteratorUnknownSize(Arrays.asList(original).iterator(), 0), false);
+    testSpliterator(original, () -> Spliterators.spliterator(original, 0), true);
+    testSpliterator(Arrays.copyOfRange(original, 1, 3), () -> Spliterators.spliterator(original, 1, 3, 0), true);
+  }
+
+  public void testDoubleSpliterator() {
+    final double[] original = {1., 2., 3., 4.};
+    testDoubleSpliterator(original, () -> Spliterators.spliterator(original, 0), true);
+    testDoubleSpliterator(Arrays.copyOfRange(original, 1, 3), () -> Spliterators.spliterator(original, 1, 3, 0), true);
+    testDoubleSpliterator(original, () -> Spliterators.spliterator(createPrimitiveDoubleIterator(original), original.length, 0), true);
+  }
+
+  public void testIntSpliterator() {
+    final int[] original = {1, 2, 3, 4};
+    testIntSpliterator(original, () -> Spliterators.spliterator(original, 0), true);
+    testIntSpliterator(Arrays.copyOfRange(original, 1, 3), () -> Spliterators.spliterator(original, 1, 3, 0), true);
+    testIntSpliterator(original, () -> Spliterators.spliterator(createPrimitiveIntIterator(original), original.length, 0), true);
+  }
+
+  public void testLongSpliterator() {
+    final long[] original = {1, 2, 3, 4};
+    testLongSpliterator(original, () -> Spliterators.spliterator(original, 0), true);
+    testLongSpliterator(Arrays.copyOfRange(original, 1, 3), () -> Spliterators.spliterator(original, 1, 3, 0), true);
+    testLongSpliterator(original, () -> Spliterators.spliterator(createPrimitiveLongIterator(original), original.length, 0), true);
+  }
+
+  public void testIterator() {
+    final String[] original = {"1", "2", "3", "4"};
+    Spliterator<String> spliterator = Spliterators.spliterator(Arrays.asList(original), 0);
+    Iterator<String> it = Spliterators.iterator(spliterator);
+
+    try {
+      it.remove();
+      fail();
+    } catch (UnsupportedOperationException expected) {
+    }
+
+    Deque<String> values = new LinkedList<>(Arrays.asList(original));
+    it.forEachRemaining(value -> assertEquals(values.pop(), value));
+    assertEquals(0, values.size());
+  }
+
+  public void testDoubleIterator() {
+    final double[] original = {1., 2., 3., 4.};
+    Spliterator.OfDouble spliterator = Spliterators.spliterator(original, 0);
+    PrimitiveIterator.OfDouble it = Spliterators.iterator(spliterator);
+
+    try {
+      it.remove();
+      fail();
+    } catch (UnsupportedOperationException expected) {
+    }
+
+    Deque<Double> values = new LinkedList<>(toDoubleCollection(original));
+    it.forEachRemaining((double value) -> assertEquals((double) values.pop(), value));
+    assertEquals(0, values.size());
+  }
+
+  public void testIntIterator() {
+    final int[] original = {1, 2, 3, 4};
+    Spliterator.OfInt spliterator = Spliterators.spliterator(original, 0);
+    PrimitiveIterator.OfInt it = Spliterators.iterator(spliterator);
+
+    try {
+      it.remove();
+      fail();
+    } catch (UnsupportedOperationException expected) {
+    }
+
+    Deque<Integer> values = new LinkedList<>(toIntCollection(original));
+    it.forEachRemaining((int value) -> assertEquals((int) values.pop(), value));
+    assertEquals(0, values.size());
+  }
+
+  public void testLongIterator() {
+    final long[] original = {1, 2, 3, 4};
+    Spliterator.OfLong spliterator = Spliterators.spliterator(original, 0);
+    PrimitiveIterator.OfLong it = Spliterators.iterator(spliterator);
+
+    try {
+      it.remove();
+      fail();
+    } catch (UnsupportedOperationException expected) {
+    }
+
+    Deque<Long> values = new LinkedList<>(toLongCollection(original));
+    it.forEachRemaining((long value) -> assertEquals((long) values.pop(), value));
+    assertEquals(0, values.size());
+  }
+
+  private <T> void testSpliterator(T[] original, Supplier<Spliterator<T>> supplier, boolean sizeKnown) {
+    Spliterator<T> spliterator = supplier.get();
+    if (sizeKnown) {
+      assertEquals(original.length, spliterator.estimateSize());
+      assertEquals(original.length, spliterator.getExactSizeIfKnown());
+    } else {
+      assertEquals(Long.MAX_VALUE, spliterator.estimateSize());
+      assertEquals(-1L, spliterator.getExactSizeIfKnown());
+    }
+
+    Deque<T> values = new LinkedList<>(Arrays.asList(original));
+    spliterator.forEachRemaining(value -> assertEquals(values.pop(), value));
+    spliterator.forEachRemaining(value -> fail());
+    assertEquals(0, values.size());
+
+    spliterator = supplier.get();
+    for (T originalValue : original) {
+      assertTrue(spliterator.tryAdvance(value -> assertEquals(originalValue, value)));
+    }
+    assertFalse(spliterator.tryAdvance(value -> fail()));
+  }
+
+  private void testDoubleSpliterator(double[] original, Supplier<Spliterator.OfDouble> supplier, boolean sizeKnown) {
+    Spliterator.OfDouble spliterator = supplier.get();
+    if (sizeKnown) {
+      assertEquals(original.length, spliterator.estimateSize());
+      assertEquals(original.length, spliterator.getExactSizeIfKnown());
+    } else {
+      assertEquals(Long.MAX_VALUE, spliterator.estimateSize());
+      assertEquals(-1L, spliterator.getExactSizeIfKnown());
+    }
+
+    Deque<Double> values = new LinkedList<>(toDoubleCollection(original));
+    spliterator.forEachRemaining((double value) -> assertEquals((double) values.pop(), value));
+    spliterator.forEachRemaining((double value) -> fail());
+    assertEquals(0, values.size());
+
+    spliterator = supplier.get();
+    for (double originalValue : original) {
+      assertTrue(spliterator.tryAdvance((double value) -> assertEquals(originalValue, value)));
+    }
+    assertFalse(spliterator.tryAdvance((double value) -> fail()));
+  }
+
+  private void testIntSpliterator(int[] original, Supplier<Spliterator.OfInt> supplier, boolean sizeKnown) {
+    Spliterator.OfInt spliterator = supplier.get();
+    if (sizeKnown) {
+      assertEquals(original.length, spliterator.estimateSize());
+      assertEquals(original.length, spliterator.getExactSizeIfKnown());
+    } else {
+      assertEquals(Long.MAX_VALUE, spliterator.estimateSize());
+      assertEquals(-1L, spliterator.getExactSizeIfKnown());
+    }
+
+    Deque<Integer> values = new LinkedList<>(toIntCollection(original));
+    spliterator.forEachRemaining((int value) -> assertEquals((int) values.pop(), value));
+    spliterator.forEachRemaining((int value) -> fail());
+    assertEquals(0, values.size());
+
+    spliterator = supplier.get();
+    for (int originalValue : original) {
+      assertTrue(spliterator.tryAdvance((int value) -> assertEquals(originalValue, value)));
+    }
+    assertFalse(spliterator.tryAdvance((int value) -> fail()));
+  }
+
+  private void testLongSpliterator(long[] original, Supplier<Spliterator.OfLong> supplier, boolean sizeKnown) {
+    Spliterator.OfLong spliterator = supplier.get();
+    if (sizeKnown) {
+      assertEquals(original.length, spliterator.estimateSize());
+      assertEquals(original.length, spliterator.getExactSizeIfKnown());
+    } else {
+      assertEquals(Long.MAX_VALUE, spliterator.estimateSize());
+      assertEquals(-1L, spliterator.getExactSizeIfKnown());
+    }
+
+    Deque<Long> values = new LinkedList<>(toLongCollection(original));
+    spliterator.forEachRemaining((long value) -> assertEquals((long) values.pop(), value));
+    spliterator.forEachRemaining((long value) -> fail());
+    assertEquals(0, values.size());
+
+    spliterator = supplier.get();
+    for (long originalValue : original) {
+      assertTrue(spliterator.tryAdvance((long value) -> assertEquals(originalValue, value)));
+    }
+    assertFalse(spliterator.tryAdvance((long value) -> fail()));
+  }
+
+  private static Collection<Double> toDoubleCollection(double[] values) {
+    ArrayList<Double> c = new ArrayList<>();
+    for (double value : values) {
+      c.add(value);
+    }
+    return c;
+  }
+
+  private static Collection<Integer> toIntCollection(int[] values) {
+    ArrayList<Integer> c = new ArrayList<>();
+    for (int value : values) {
+      c.add(value);
+    }
+    return c;
+  }
+
+  private static Collection<Long> toLongCollection(long[] values) {
+    ArrayList<Long> c = new ArrayList<>();
+    for (long value : values) {
+      c.add(value);
+    }
+    return c;
+  }
+
+  private static PrimitiveIterator.OfDouble createPrimitiveDoubleIterator(double[] values) {
+    final Iterator<Double> it = toDoubleCollection(values).iterator();
+    return new PrimitiveIterator.OfDouble() {
+      @Override
+      public double nextDouble() {
+        return it.next();
+      }
+
+      @Override
+      public boolean hasNext() {
+        return it.hasNext();
+      }
+    };
+  }
+
+  private static PrimitiveIterator.OfInt createPrimitiveIntIterator(int[] values) {
+    final Iterator<Integer> it = toIntCollection(values).iterator();
+    return new PrimitiveIterator.OfInt() {
+      @Override
+      public int nextInt() {
+        return it.next();
+      }
+
+      @Override
+      public boolean hasNext() {
+        return it.hasNext();
+      }
+    };
+  }
+
+  private static PrimitiveIterator.OfLong createPrimitiveLongIterator(long[] values) {
+    final Iterator<Long> it = toLongCollection(values).iterator();
+    return new PrimitiveIterator.OfLong() {
+      @Override
+      public long nextLong() {
+        return it.next();
+      }
+
+      @Override
+      public boolean hasNext() {
+        return it.hasNext();
+      }
+    };
+  }
+
+}