Add java.lang.Iterable.spliterator() and supporting classes.

The only android specific changes here are to HashMap - their
spliterators add Spliterator.ORDERED if the HashMap is an instance
of a LinkedHashMap.

There's some degree of duplication between the test code used to
exercise primitive arrays (Arrays / Spliterators provide similar
APIs). These differences are quite hard to consolidate, since the
Arrays version declares that their Spliterators are ORDERED but
the Spliterator version doesn't. The latter also allows us to declare
additional Spliterator characteristics.

bug: 27426688
Change-Id: I191a9319d4af7e22834f2d91f73634a227b36bc2
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index 9b5439e..ff2a40b 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -1518,5 +1518,14 @@
     "org.apache.harmony.tests.javax.security.OldSHA1PRNGSecureRandomTest#testNextBytesbyteArray04",
     "org.apache.harmony.tests.javax.security.OldSHA1PRNGSecureRandomTest#testSetSeedbyteArray03"
   ]
+},
+{
+  description: "Failures in spliterator tests",
+  bug: 27539473,
+  result: EXEC_FAILED,
+  names: [
+    "libcore.java.util.SpliteratorsTest#test_primitive_spliterators_NPE",
+    "org.apache.harmony.tests.java.util.ArraysTest#test_primitive_spliterators_NPE"
+  ]
 }
 ]
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayDequeTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayDequeTest.java
index 71f7d5a..b0fc896 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayDequeTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayDequeTest.java
@@ -20,14 +20,17 @@
 import java.io.Serializable;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.ConcurrentModificationException;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.NoSuchElementException;
+import java.util.Spliterator;
 
 import junit.framework.TestCase;
 
 import libcore.java.util.ForEachRemainingTester;
+import libcore.java.util.SpliteratorTester;
 import org.apache.harmony.testframework.serialization.SerializationTest;
 import org.apache.harmony.testframework.serialization.SerializationTest.SerializableAssert;
 
@@ -909,6 +912,36 @@
         adq.iterator().forEachRemaining(s -> adq.add(s));
     }
 
+    public void test_spliterator() throws Exception {
+        ArrayList<Integer> testElements = new ArrayList<>(
+                Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16));
+        ArrayDeque<Integer> adq = new ArrayDeque<>();
+        adq.addAll(testElements);
+
+        SpliteratorTester.runBasicIterationTests(adq.spliterator(), testElements);
+        SpliteratorTester.runBasicSplitTests(adq, testElements);
+        SpliteratorTester.testSpliteratorNPE(adq.spliterator());
+
+        assertTrue(adq.spliterator().hasCharacteristics(
+                Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED));
+
+        SpliteratorTester.runOrderedTests(adq);
+        SpliteratorTester.runSizedTests(adq, 16 /* expected size */);
+        SpliteratorTester.runSubSizedTests(adq, 16 /* expected size */);
+    }
+
+    public void test_spliterator_CME() throws Exception {
+        ArrayDeque<Integer> adq = new ArrayDeque<>();
+        adq.add(52);
+
+        Spliterator<Integer> sp = adq.spliterator();
+
+        // Spliterators from ArrayDequeues never throw CME. The following statements
+        // would have thrown a CME on most other collection classes.
+        assertTrue(sp.tryAdvance(value -> adq.add(value)));
+        sp.forEachRemaining(value -> adq.add(value));
+    }
+
     /**
      * java.util.ArrayDeque#Serialization()
      */
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayListTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayListTest.java
index 4d380af..446f61c 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayListTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayListTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.harmony.tests.java.util;
 
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -24,8 +25,10 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.util.Spliterator;
 import java.util.Vector;
 
+import libcore.java.util.SpliteratorTester;
 import tests.support.Support_ListTest;
 import libcore.java.util.ForEachRemainingTester;
 
@@ -1095,7 +1098,45 @@
 
     public void test_forEachRemaining_iterator() throws Exception {
         ForEachRemainingTester.runTests(ArrayList.class, new String[] { "foo", "bar", "baz"});
-        ForEachRemainingTester.runTests(ArrayList.class, new String[] { "foo" });
+        ForEachRemainingTester.runTests(ArrayList.class, new String[]{"foo"});
+    }
+
+    public void test_spliterator() throws Exception {
+        ArrayList<Integer> testElements = new ArrayList<>(
+                Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16));
+        ArrayList<Integer> list = new ArrayList<>();
+        list.addAll(testElements);
+
+        SpliteratorTester.runBasicIterationTests(list.spliterator(), testElements);
+        SpliteratorTester.runBasicSplitTests(list, testElements);
+        SpliteratorTester.testSpliteratorNPE(list.spliterator());
+
+        assertTrue(list.spliterator().hasCharacteristics(
+                Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED));
+
+        SpliteratorTester.runOrderedTests(list);
+        SpliteratorTester.runSizedTests(list, 16 /* expected size */);
+        SpliteratorTester.runSubSizedTests(list, 16 /* expected size */);
+    }
+
+    public void test_spliterator_CME() throws Exception {
+        ArrayList<Integer> list = new ArrayList<>();
+        list.add(52);
+
+        Spliterator<Integer> sp = list.spliterator();
+        try {
+            sp.tryAdvance(value -> list.add(value));
+            fail();
+        } catch (ConcurrentModificationException expected) {
+        }
+
+        list = new ArrayList<>();
+        list.add(52);
+        try {
+            sp.forEachRemaining(value -> list.add(value));
+            fail();
+        } catch (ConcurrentModificationException expected) {
+        }
     }
 
     /**
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArraysTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArraysTest.java
index 8b4844c..c6bcd3d 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArraysTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArraysTest.java
@@ -16,14 +16,22 @@
  */
 package org.apache.harmony.tests.java.util;
 
+import libcore.java.util.SpliteratorTester;
 import tests.support.Support_UnmodifiableCollectionTest;
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Random;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
 
 public class ArraysTest extends junit.framework.TestCase {
 
@@ -4190,6 +4198,357 @@
                 Arrays.copyOfRange(objArray, 0, 0, LinkedList[].class).length);
     }
 
+    public void test_asList_spliterator() {
+        List<String> list = Arrays.asList(
+                "a", "b", "c", "d", "e", "f", "g", "h",
+                "i", "j", "k", "l", "m", "n", "o", "p");
+        ArrayList<String> expected = new ArrayList<>(list);
+
+        SpliteratorTester.runBasicIterationTests(list.spliterator(), expected);
+        SpliteratorTester.runBasicSplitTests(list, expected);
+        SpliteratorTester.testSpliteratorNPE(list.spliterator());
+
+        assertTrue(list.spliterator().hasCharacteristics(Spliterator.ORDERED));
+
+        SpliteratorTester.runOrderedTests(list);
+    }
+
+    public void test_spliterator_ref() {
+        String[] elements = {
+                "a", "b", "c", "d", "e", "f", "g", "h",
+                "i", "j", "k", "l", "m", "n", "o", "p" };
+
+        ArrayList<String> expected = new ArrayList<>(Arrays.asList(elements));
+
+        SpliteratorTester.runBasicIterationTests(Arrays.spliterator(elements), expected);
+        SpliteratorTester.testSpliteratorNPE(Arrays.spliterator(elements));
+
+        Spliterator<String> sp = Arrays.spliterator(elements);
+        assertTrue(sp.hasCharacteristics(Spliterator.ORDERED));
+
+        // Basic split tests.
+        Spliterator<String> sp1 =  sp.trySplit();
+        assertNotNull(sp1);
+
+        ArrayList<String> recorder = new ArrayList<>();
+        sp1.forEachRemaining(value -> recorder.add(value));
+        sp.forEachRemaining(value -> recorder.add(value));
+        Collections.sort(recorder);
+        assertEquals(expected, recorder);
+    }
+
+    public void test_spliterator_ref_bounds() {
+        String[] elements = { "BAD", "EVIL", "a", "b", "c", "d", "e", "f", "g", "h",
+                "i", "j", "k", "l", "m", "n", "o", "p", "DO", "NOT", "INCLUDE" };
+
+        ArrayList<String> expected = new ArrayList<>(
+                Arrays.asList(Arrays.copyOfRange(elements, 2, 16)));
+
+        SpliteratorTester.runBasicIterationTests(Arrays.spliterator(elements, 2, 16), expected);
+        SpliteratorTester.testSpliteratorNPE(Arrays.spliterator(elements, 2, 16));
+
+        Spliterator<String> sp = Arrays.spliterator(elements, 2, 16);
+        assertTrue(sp.hasCharacteristics(Spliterator.ORDERED));
+
+        // Basic split tests.
+        Spliterator<String> sp1 =  sp.trySplit();
+        assertNotNull(sp1);
+
+        ArrayList<String> recorder = new ArrayList<>();
+        sp1.forEachRemaining(value -> recorder.add(value));
+        sp.forEachRemaining(value -> recorder.add(value));
+        Collections.sort(recorder);
+        assertEquals(expected, recorder);
+    }
+
+    private static class PrimitiveIntArrayList {
+        final int[] array;
+        int idx;
+
+        PrimitiveIntArrayList(int size) {
+            array = new int[size];
+        }
+
+        public void add(int element) {
+            array[idx++] = element;
+        }
+
+        public int[] toSortedArray() {
+            Arrays.sort(array);
+            return array;
+        }
+    }
+
+    private static class PrimitiveLongArrayList {
+        final long[] array;
+        int idx;
+
+        PrimitiveLongArrayList(int size) {
+            array = new long[size];
+        }
+
+        public void add(long element) {
+            array[idx++] = element;
+        }
+
+        public long[] toSortedArray() {
+            Arrays.sort(array);
+            return array;
+        }
+    }
+
+    private static class PrimitiveDoubleArrayList {
+        final double[] array;
+        int idx;
+
+        PrimitiveDoubleArrayList(int size) {
+            array = new double[size];
+        }
+
+        public void add(double element) {
+            array[idx++] = element;
+        }
+
+        public double[] toSortedArray() {
+            Arrays.sort(array);
+            return array;
+        }
+    }
+
+    public void test_spliterator_int() {
+        int[] elements = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+
+        Spliterator.OfInt intSp = Arrays.spliterator(elements);
+
+        assertEquals(16, intSp.estimateSize());
+        assertEquals(16, intSp.getExactSizeIfKnown());
+        assertTrue(intSp.hasCharacteristics(Spliterator.ORDERED));
+
+        assertTrue(intSp.tryAdvance((Integer value) -> assertEquals(1, (int) value)));
+        assertTrue(intSp.tryAdvance((int value) -> assertEquals(2, (int) value)));
+
+        PrimitiveIntArrayList recorder = new PrimitiveIntArrayList(16);
+        // Record elements observed by previous tests.
+        recorder.add(1);
+        recorder.add(2);
+
+        Spliterator.OfInt split1 = intSp.trySplit();
+        assertNotNull(split1);
+        assertTrue(split1.tryAdvance((int value) -> recorder.add(value)));
+        assertTrue(split1.tryAdvance((Integer value) -> recorder.add(value)));
+
+        // Assert that splits can themselves resplit.
+        Spliterator.OfInt split2 = split1.trySplit();
+        assertNotNull(split2);
+        split2.forEachRemaining((int value) -> recorder.add(value));
+        assertFalse(split2.tryAdvance((int value) -> fail()));
+        assertFalse(split2.tryAdvance((Integer value) -> fail()));
+
+        // Iterate over the remaning elements so we can make sure we've looked at
+        // everything.
+        split1.forEachRemaining((int value) -> recorder.add(value));
+        intSp.forEachRemaining((int value) -> recorder.add(value));
+
+        int[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(elements), Arrays.toString(recorded));
+    }
+
+    public void test_spliterator_intOffsetBasic() {
+        int[] elements = { 123123, 131321312, 1, 2, 3, 4, 32323232, 45454};
+        Spliterator.OfInt sp = Arrays.spliterator(elements, 2, 6);
+
+        PrimitiveIntArrayList recorder = new PrimitiveIntArrayList(4);
+        sp.tryAdvance((Integer value) -> recorder.add((int) value));
+        sp.tryAdvance((int value) -> recorder.add(value));
+        sp.forEachRemaining((int value) -> recorder.add(value));
+
+        int[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(new int[] { 1, 2, 3, 4 }), Arrays.toString(recorded));
+    }
+
+    public void test_spliterator_longOffsetBasic() {
+        long[] elements = { 123123, 131321312, 1, 2, 3, 4, 32323232, 45454};
+        Spliterator.OfLong sp = Arrays.spliterator(elements, 2, 6);
+
+        PrimitiveLongArrayList recorder = new PrimitiveLongArrayList(4);
+        sp.tryAdvance((Long value) -> recorder.add((long) value));
+        sp.tryAdvance((long value) -> recorder.add(value));
+        sp.forEachRemaining((long value) -> recorder.add(value));
+
+        long[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(new long[] { 1, 2, 3, 4 }), Arrays.toString(recorded));
+    }
+
+    public void test_spliterator_doubleOffsetBasic() {
+        double[] elements = { 123123, 131321312, 1, 2, 3, 4, 32323232, 45454};
+        Spliterator.OfDouble sp = Arrays.spliterator(elements, 2, 6);
+
+        PrimitiveDoubleArrayList recorder = new PrimitiveDoubleArrayList(4);
+        sp.tryAdvance((Double value) -> recorder.add((double) value));
+        sp.tryAdvance((double value) -> recorder.add(value));
+        sp.forEachRemaining((double value) -> recorder.add(value));
+
+        double[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(new double[] { 1, 2, 3, 4 }), Arrays.toString(recorded));
+    }
+
+    public void test_spliterator_long() {
+        long[] elements = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+
+        Spliterator.OfLong longSp = Arrays.spliterator(elements);
+
+        assertEquals(16, longSp.estimateSize());
+        assertEquals(16, longSp.getExactSizeIfKnown());
+        assertTrue(longSp.hasCharacteristics(Spliterator.ORDERED));
+
+        assertTrue(longSp.tryAdvance((Long value) -> assertEquals(1, (long) value)));
+        assertTrue(longSp.tryAdvance((long value) -> assertEquals(2, (long) value)));
+
+        PrimitiveLongArrayList recorder = new PrimitiveLongArrayList(16);
+        // Record elements observed by previous tests.
+        recorder.add(1);
+        recorder.add(2);
+
+        Spliterator.OfLong split1 = longSp.trySplit();
+        assertNotNull(split1);
+        assertTrue(split1.tryAdvance((long value) -> recorder.add(value)));
+        assertTrue(split1.tryAdvance((Long value) -> recorder.add(value)));
+
+        // Assert that splits can themselves resplit.
+        Spliterator.OfLong split2 = split1.trySplit();
+        assertNotNull(split2);
+        split2.forEachRemaining((long value) -> recorder.add(value));
+        assertFalse(split2.tryAdvance((long value) -> fail()));
+        assertFalse(split2.tryAdvance((Long value) -> fail()));
+
+        // Iterate over the remaning elements so we can make sure we've looked at
+        // everything.
+        split1.forEachRemaining((long value) -> recorder.add(value));
+        longSp.forEachRemaining((long value) -> recorder.add(value));
+
+        long[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(elements), Arrays.toString(recorded));
+    }
+
+
+    public void test_spliterator_double() {
+        double[] elements = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+
+        Spliterator.OfDouble doubleSp = Arrays.spliterator(elements);
+
+        assertEquals(16, doubleSp.estimateSize());
+        assertEquals(16, doubleSp.getExactSizeIfKnown());
+        assertTrue(doubleSp.hasCharacteristics(Spliterator.ORDERED));
+
+        assertTrue(doubleSp.tryAdvance((Double value) -> assertEquals(1.0, (double) value)));
+        assertTrue(doubleSp.tryAdvance((double value) -> assertEquals(2.0, (double) value)));
+
+        PrimitiveDoubleArrayList recorder = new PrimitiveDoubleArrayList(16);
+        // Record elements observed by previous tests.
+        recorder.add(1);
+        recorder.add(2);
+
+        Spliterator.OfDouble split1 = doubleSp.trySplit();
+        assertNotNull(split1);
+        assertTrue(split1.tryAdvance((double value) -> recorder.add(value)));
+        assertTrue(split1.tryAdvance((Double value) -> recorder.add(value)));
+
+        // Assert that splits can themselves resplit.
+        Spliterator.OfDouble split2 = split1.trySplit();
+        assertNotNull(split2);
+        split2.forEachRemaining((double value) -> recorder.add(value));
+        assertFalse(split2.tryAdvance((double value) -> fail()));
+        assertFalse(split2.tryAdvance((Double value) -> fail()));
+
+        // Iterate over the remaining elements so we can make sure we've looked at
+        // everything.
+        split1.forEachRemaining((double value) -> recorder.add(value));
+        doubleSp.forEachRemaining((double value) -> recorder.add(value));
+
+        double[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(elements), Arrays.toString(recorded));
+    }
+
+    public void test_primitive_spliterators_NPE() {
+        final int[] elements = { 1, 2, 3, 4, 5, 6};
+        Spliterator.OfInt intSp = Arrays.spliterator(elements);
+        try {
+            intSp.forEachRemaining((Consumer<Integer>) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            intSp.tryAdvance((Consumer<Integer>) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            intSp.forEachRemaining((IntConsumer) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            intSp.tryAdvance((IntConsumer) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        final long[] longElements = { 1, 2, 3, 4, 5, 6};
+        Spliterator.OfLong longSp = Arrays.spliterator(longElements);
+        try {
+            longSp.forEachRemaining((Consumer<Long>) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            longSp.tryAdvance((Consumer<Long>) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            longSp.forEachRemaining((LongConsumer) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            longSp.tryAdvance((LongConsumer) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        final double[] doubleElements = { 1, 2, 3, 4, 5, 6};
+        Spliterator.OfDouble doubleSp = Arrays.spliterator(doubleElements);
+        try {
+            doubleSp.forEachRemaining((Consumer<Double>) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            doubleSp.tryAdvance((Consumer<Double>) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            doubleSp.forEachRemaining((DoubleConsumer) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            doubleSp.tryAdvance((DoubleConsumer) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
     /**
      * Tears down the fixture, for example, close a network connection. This
      * method is called after a test is executed.
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashMapTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashMapTest.java
index eb2bb47..adf6620 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashMapTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashMapTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.harmony.tests.java.util;
 
+import libcore.java.util.SpliteratorTester;
 import org.apache.harmony.testframework.serialization.SerializationTest;
 import tests.support.Support_MapTest2;
 import tests.support.Support_UnmodifiableCollectionTest;
@@ -27,6 +28,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.ConcurrentModificationException;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -34,6 +36,7 @@
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
+import java.util.Spliterator;
 import java.util.TreeMap;
 
 public class HashMapTest extends junit.framework.TestCase {
@@ -815,6 +818,104 @@
         }
     }
 
+    public void test_spliterator_keySet() {
+        HashMap<String, String> hashMap = new HashMap<>();
+        hashMap.put("a", "1");
+        hashMap.put("b", "2");
+        hashMap.put("c", "3");
+        hashMap.put("d", "4");
+        hashMap.put("e", "5");
+        hashMap.put("f", "6");
+        hashMap.put("g", "7");
+        hashMap.put("h", "8");
+        hashMap.put("i", "9");
+        hashMap.put("j", "10");
+        hashMap.put("k", "11");
+        hashMap.put("l", "12");
+        hashMap.put("m", "13");
+        hashMap.put("n", "14");
+        hashMap.put("o", "15");
+        hashMap.put("p", "16");
+
+        Set<String> keys = hashMap.keySet();
+        ArrayList<String> expectedKeys = new ArrayList<>(keys);
+
+        SpliteratorTester.runBasicIterationTests(keys.spliterator(), expectedKeys);
+        SpliteratorTester.runBasicSplitTests(keys, expectedKeys);
+        SpliteratorTester.testSpliteratorNPE(keys.spliterator());
+
+        assertTrue(keys.spliterator().hasCharacteristics(Spliterator.SIZED | Spliterator.DISTINCT));
+
+        SpliteratorTester.runSizedTests(keys.spliterator(), 16);
+        SpliteratorTester.runDistinctTests(keys);
+    }
+
+    public void test_spliterator_valueSet() {
+        HashMap<String, String> hashMap = new HashMap<>();
+        hashMap.put("a", "1");
+        hashMap.put("b", "2");
+        hashMap.put("c", "3");
+        hashMap.put("d", "4");
+        hashMap.put("e", "5");
+        hashMap.put("f", "6");
+        hashMap.put("g", "7");
+        hashMap.put("h", "8");
+        hashMap.put("i", "9");
+        hashMap.put("j", "10");
+        hashMap.put("k", "11");
+        hashMap.put("l", "12");
+        hashMap.put("m", "13");
+        hashMap.put("n", "14");
+        hashMap.put("o", "15");
+        hashMap.put("p", "16");
+
+        Collection<String> values = hashMap.values();
+        ArrayList<String> expectedValues = new ArrayList<>(values);
+
+        SpliteratorTester.runBasicIterationTests(values.spliterator(), expectedValues);
+        SpliteratorTester.runBasicSplitTests(values, expectedValues);
+        SpliteratorTester.testSpliteratorNPE(values.spliterator());
+
+        assertTrue(values.spliterator().hasCharacteristics(Spliterator.SIZED));
+
+        SpliteratorTester.runSizedTests(values.spliterator(), 16);
+    }
+
+    public void test_spliterator_entrySet() {
+        HashMap<String, String> hashMap = new HashMap<>();
+        hashMap.put("a", "1");
+        hashMap.put("b", "2");
+        hashMap.put("c", "3");
+        hashMap.put("d", "4");
+        hashMap.put("e", "5");
+        hashMap.put("f", "6");
+        hashMap.put("g", "7");
+        hashMap.put("h", "8");
+        hashMap.put("i", "9");
+        hashMap.put("j", "10");
+        hashMap.put("k", "11");
+        hashMap.put("l", "12");
+        hashMap.put("m", "13");
+        hashMap.put("n", "14");
+        hashMap.put("o", "15");
+        hashMap.put("p", "16");
+
+        Set<Map.Entry<String, String>> values = hashMap.entrySet();
+        ArrayList<Map.Entry<String, String>> expectedValues = new ArrayList<>(values);
+
+        Comparator<Map.Entry<String, String>> comparator =
+                (a, b) -> (a.getKey().compareTo(b.getKey()));
+
+        SpliteratorTester.runBasicIterationTests(values.spliterator(), expectedValues);
+        SpliteratorTester.runBasicSplitTests(values, expectedValues, comparator);
+        SpliteratorTester.testSpliteratorNPE(values.spliterator());
+
+        assertTrue(values.spliterator().hasCharacteristics(Spliterator.SIZED | Spliterator.DISTINCT));
+
+        SpliteratorTester.runSizedTests(values.spliterator(), 16);
+        SpliteratorTester.runDistinctTests(values);
+    }
+
     /**
      * Sets up the fixture, for example, open a network connection. This method
      * is called before a test is executed.
@@ -864,3 +965,4 @@
         SerializationTest.verifySelf(hm);
     }
 }
+
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashSetTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashSetTest.java
index 7add356..95aeb6e 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashSetTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashSetTest.java
@@ -17,10 +17,13 @@
 
 package org.apache.harmony.tests.java.util;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.ConcurrentModificationException;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 import java.io.ObjectOutputStream;
 import java.io.ByteArrayOutputStream;
@@ -28,7 +31,9 @@
 import java.io.Serializable;
 import java.lang.reflect.Method;
 import java.lang.reflect.InvocationTargetException;
+import java.util.Spliterator;
 
+import libcore.java.util.SpliteratorTester;
 import org.apache.harmony.testframework.serialization.SerializationTest;
 
 public class HashSetTest extends junit.framework.TestCase {
@@ -256,6 +261,22 @@
         } catch(ConcurrentModificationException expected) {}
     }
 
+    public void test_spliterator() throws Exception {
+        HashSet<String> hashSet = new HashSet<>();
+        List<String> keys = Arrays.asList(
+                "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p");
+        hashSet.addAll(keys);
+
+        ArrayList<String> expectedKeys = new ArrayList<>(keys);
+        SpliteratorTester.runBasicIterationTests_unordered(hashSet.spliterator(), expectedKeys,
+                String::compareTo);
+        SpliteratorTester.runBasicSplitTests(hashSet, expectedKeys);
+        SpliteratorTester.testSpliteratorNPE(hashSet.spliterator());
+
+        assertTrue(hashSet.spliterator().hasCharacteristics(Spliterator.DISTINCT));
+        SpliteratorTester.runDistinctTests(keys);
+    }
+
     /**
      * Sets up the fixture, for example, open a network connection. This method
      * is called before a test is executed.
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/IdentityHashMapTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/IdentityHashMapTest.java
index bb85ec6..bf8db02 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/IdentityHashMapTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/IdentityHashMapTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.harmony.tests.java.util;
 
+import libcore.java.util.SpliteratorTester;
 import org.apache.harmony.testframework.serialization.SerializationTest;
 import org.apache.harmony.testframework.serialization.SerializationTest.SerializableAssert;
 import tests.support.Support_MapTest2;
@@ -25,6 +26,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.ConcurrentModificationException;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -968,6 +970,91 @@
         assertEquals(1, outputMap.size());
     }
 
+    public void test_spliterator_keySet() {
+        IdentityHashMap<String, String> hashMap = new IdentityHashMap<>();
+        hashMap.put("a", "1");
+        hashMap.put("b", "2");
+        hashMap.put("c", "3");
+        hashMap.put("d", "4");
+        hashMap.put("e", "5");
+        hashMap.put("f", "6");
+        hashMap.put("g", "7");
+        hashMap.put("h", "8");
+        hashMap.put("i", "9");
+        hashMap.put("j", "10");
+        hashMap.put("k", "11");
+        hashMap.put("l", "12");
+        hashMap.put("m", "13");
+        hashMap.put("n", "14");
+        hashMap.put("o", "15");
+        hashMap.put("p", "16");
+
+        Set<String> keys = hashMap.keySet();
+        ArrayList<String> expectedKeys = new ArrayList<>(keys);
+
+        SpliteratorTester.runBasicIterationTests(keys.spliterator(), expectedKeys);
+        SpliteratorTester.runBasicSplitTests(keys, expectedKeys);
+        SpliteratorTester.testSpliteratorNPE(keys.spliterator());
+    }
+
+    public void test_spliterator_valueSet() {
+        IdentityHashMap<String, String> hashMap = new IdentityHashMap<>();
+        hashMap.put("a", "1");
+        hashMap.put("b", "2");
+        hashMap.put("c", "3");
+        hashMap.put("d", "4");
+        hashMap.put("e", "5");
+        hashMap.put("f", "6");
+        hashMap.put("g", "7");
+        hashMap.put("h", "8");
+        hashMap.put("i", "9");
+        hashMap.put("j", "10");
+        hashMap.put("k", "11");
+        hashMap.put("l", "12");
+        hashMap.put("m", "13");
+        hashMap.put("n", "14");
+        hashMap.put("o", "15");
+        hashMap.put("p", "16");
+
+        Collection<String> values = hashMap.values();
+        ArrayList<String> expectedValues = new ArrayList<>(values);
+
+        SpliteratorTester.runBasicIterationTests(values.spliterator(), expectedValues);
+        SpliteratorTester.runBasicSplitTests(values, expectedValues);
+        SpliteratorTester.testSpliteratorNPE(values.spliterator());
+    }
+
+    public void test_spliterator_entrySet() {
+        IdentityHashMap<String, String> hashMap = new IdentityHashMap<>();
+        hashMap.put("a", "1");
+        hashMap.put("b", "2");
+        hashMap.put("c", "3");
+        hashMap.put("d", "4");
+        hashMap.put("e", "5");
+        hashMap.put("f", "6");
+        hashMap.put("g", "7");
+        hashMap.put("h", "8");
+        hashMap.put("i", "9");
+        hashMap.put("j", "10");
+        hashMap.put("k", "11");
+        hashMap.put("l", "12");
+        hashMap.put("m", "13");
+        hashMap.put("n", "14");
+        hashMap.put("o", "15");
+        hashMap.put("p", "16");
+
+        Set<Map.Entry<String, String>> values = hashMap.entrySet();
+        ArrayList<Map.Entry<String, String>> expectedValues = new ArrayList<>(values);
+
+        Comparator<Map.Entry<String, String>> comparator =
+                (a, b) -> (a.getKey().compareTo(b.getKey()));
+
+        SpliteratorTester.runBasicIterationTests(values.spliterator(), expectedValues);
+        SpliteratorTester.runBasicSplitTests(values, expectedValues, comparator);
+        SpliteratorTester.testSpliteratorNPE(values.spliterator());
+    }
+
+
     // comparator for IdentityHashMap objects
     private static final SerializableAssert COMPARATOR = new SerializableAssert() {
         public void assertDeserialized(Serializable initial,
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedHashMapTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedHashMapTest.java
index 05873f6..79cd106 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedHashMapTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedHashMapTest.java
@@ -21,14 +21,18 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.ConcurrentModificationException;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.Spliterator;
 import java.util.TreeMap;
 
+import libcore.java.util.SpliteratorTester;
 import tests.support.Support_MapTest2;
 import tests.support.Support_UnmodifiableCollectionTest;
 
@@ -817,6 +821,103 @@
         assertEquals(1, outputMap.size());
     }
 
+    public void test_spliterator_keySet() {
+        LinkedHashMap<String, String> hashMap = new LinkedHashMap<>();
+        hashMap.put("a", "1");
+        hashMap.put("b", "2");
+        hashMap.put("c", "3");
+        hashMap.put("d", "4");
+        hashMap.put("e", "5");
+        hashMap.put("f", "6");
+        hashMap.put("g", "7");
+        hashMap.put("h", "8");
+        hashMap.put("i", "9");
+        hashMap.put("j", "10");
+        hashMap.put("k", "11");
+        hashMap.put("l", "12");
+        hashMap.put("m", "13");
+        hashMap.put("n", "14");
+        hashMap.put("o", "15");
+        hashMap.put("p", "16");
+
+        Set<String> keys = hashMap.keySet();
+        ArrayList<String> expectedKeys = new ArrayList<>(keys);
+
+        SpliteratorTester.runBasicIterationTests_unordered(keys.spliterator(), expectedKeys,
+                String::compareTo);
+        SpliteratorTester.runBasicSplitTests(keys, expectedKeys);
+        SpliteratorTester.testSpliteratorNPE(keys.spliterator());
+
+        assertTrue(keys.spliterator().hasCharacteristics(Spliterator.ORDERED));
+        SpliteratorTester.runOrderedTests(keys);
+    }
+
+    public void test_spliterator_valueSet() {
+        LinkedHashMap<String, String> hashMap = new LinkedHashMap<>();
+        hashMap.put("a", "1");
+        hashMap.put("b", "2");
+        hashMap.put("c", "3");
+        hashMap.put("d", "4");
+        hashMap.put("e", "5");
+        hashMap.put("f", "6");
+        hashMap.put("g", "7");
+        hashMap.put("h", "8");
+        hashMap.put("i", "9");
+        hashMap.put("j", "10");
+        hashMap.put("k", "11");
+        hashMap.put("l", "12");
+        hashMap.put("m", "13");
+        hashMap.put("n", "14");
+        hashMap.put("o", "15");
+        hashMap.put("p", "16");
+
+        Collection<String> values = hashMap.values();
+        ArrayList<String> expectedValues = new ArrayList<>(values);
+
+        SpliteratorTester.runBasicIterationTests_unordered(values.spliterator(), expectedValues,
+                String::compareTo);
+        SpliteratorTester.runBasicSplitTests(values, expectedValues);
+        SpliteratorTester.testSpliteratorNPE(values.spliterator());
+
+        assertTrue(values.spliterator().hasCharacteristics(Spliterator.ORDERED));
+        SpliteratorTester.runOrderedTests(values);
+    }
+
+    public void test_spliterator_entrySet() {
+        LinkedHashMap<String, String> hashMap = new LinkedHashMap<>();
+        hashMap.put("a", "1");
+        hashMap.put("b", "2");
+        hashMap.put("c", "3");
+        hashMap.put("d", "4");
+        hashMap.put("e", "5");
+        hashMap.put("f", "6");
+        hashMap.put("g", "7");
+        hashMap.put("h", "8");
+        hashMap.put("i", "9");
+        hashMap.put("j", "10");
+        hashMap.put("k", "11");
+        hashMap.put("l", "12");
+        hashMap.put("m", "13");
+        hashMap.put("n", "14");
+        hashMap.put("o", "15");
+        hashMap.put("p", "16");
+
+        Set<Map.Entry<String, String>> entries = hashMap.entrySet();
+        ArrayList<Map.Entry<String, String>> expectedValues = new ArrayList<>(entries);
+
+        Comparator<Map.Entry<String, String>> comparator =
+                (a, b) -> (a.getKey().compareTo(b.getKey()));
+
+        SpliteratorTester.runBasicIterationTests_unordered(entries.spliterator(), expectedValues,
+                (a, b) -> (a.getKey().compareTo(b.getKey())));
+        SpliteratorTester.runBasicSplitTests(entries, expectedValues, comparator);
+        SpliteratorTester.testSpliteratorNPE(entries.spliterator());
+
+        assertTrue(entries.spliterator().hasCharacteristics(Spliterator.ORDERED));
+        SpliteratorTester.runOrderedTests(entries);
+    }
+
+
     /**
      * Sets up the fixture, for example, open a network connection. This method
      * is called before a test is executed.
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedHashSetTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedHashSetTest.java
index 721a879..f3340d1 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedHashSetTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedHashSetTest.java
@@ -16,11 +16,17 @@
 
 package org.apache.harmony.tests.java.util;
 
+import libcore.java.util.SpliteratorTester;
+
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Set;
+import java.util.Spliterator;
 import java.util.Vector;
 
 /**
@@ -325,6 +331,25 @@
         }
     }
 
+    public void test_spliterator() throws Exception {
+        LinkedHashSet<String> hashSet = new LinkedHashSet<>();
+        List<String> keys = Arrays.asList(
+                "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p");
+        hashSet.addAll(keys);
+
+        ArrayList<String> expectedKeys = new ArrayList<>(keys);
+        SpliteratorTester.runBasicIterationTests_unordered(hashSet.spliterator(), expectedKeys,
+                String::compareTo);
+        SpliteratorTester.runBasicSplitTests(hashSet, expectedKeys);
+        SpliteratorTester.testSpliteratorNPE(hashSet.spliterator());
+
+        assertTrue(hashSet.spliterator().hasCharacteristics(Spliterator.ORDERED));
+        SpliteratorTester.runOrderedTests(keys);
+
+        assertTrue(hashSet.spliterator().hasCharacteristics(Spliterator.DISTINCT));
+        SpliteratorTester.runDistinctTests(keys);
+    }
+
     /**
      * Sets up the fixture, for example, open a network connection. This method
      * is called before a test is executed.
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedListTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedListTest.java
index 4d48e7b..1904aa9 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedListTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedListTest.java
@@ -27,9 +27,11 @@
 import java.util.List;
 import java.util.ListIterator;
 import java.util.NoSuchElementException;
+import java.util.Spliterator;
 import java.util.Vector;
 
 import libcore.java.util.ForEachRemainingTester;
+import libcore.java.util.SpliteratorTester;
 import org.apache.harmony.testframework.serialization.SerializationTest;
 import org.apache.harmony.testframework.serialization.SerializationTest.SerializableAssert;
 
@@ -944,6 +946,42 @@
         ForEachRemainingTester.runTests(LinkedList.class, new String[] { "foo" });
     }
 
+    public void test_spliterator() throws Exception {
+        ArrayList<Integer> testElements = new ArrayList<>(
+                Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16));
+        LinkedList<Integer> list = new LinkedList<>();
+        list.addAll(testElements);
+
+        SpliteratorTester.runBasicIterationTests(list.spliterator(), testElements);
+        SpliteratorTester.runBasicSplitTests(list, testElements);
+        SpliteratorTester.testSpliteratorNPE(list.spliterator());
+
+        assertTrue(list.spliterator().hasCharacteristics(
+                Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED));
+
+        SpliteratorTester.runOrderedTests(list);
+        SpliteratorTester.runSizedTests(list, 16 /* expected size */);
+        SpliteratorTester.runSubSizedTests(list, 16 /* expected size */);
+    }
+
+    public void test_spliterator_CME() throws Exception {
+        LinkedList<Integer> list = new LinkedList<>();
+        list.add(52);
+
+        Spliterator<Integer> sp = list.spliterator();
+        try {
+            sp.tryAdvance(value -> list.add(value));
+            fail();
+        } catch (ConcurrentModificationException expected) {
+        }
+
+        try {
+            sp.forEachRemaining(value -> list.add(value));
+            fail();
+        } catch (ConcurrentModificationException expected) {
+        }
+    }
+
     /**
      * java.util.LinkedList#Serialization()
      */
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/PriorityQueueTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/PriorityQueueTest.java
index 9c9d4fe..d4061dc 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/PriorityQueueTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/PriorityQueueTest.java
@@ -20,14 +20,18 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Comparator;
+import java.util.ConcurrentModificationException;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.PriorityQueue;
 import java.util.SortedSet;
+import java.util.Spliterator;
 import java.util.TreeSet;
 
 import junit.framework.TestCase;
+import libcore.java.util.SpliteratorTester;
 import tests.util.SerializationTester;
 
 public class PriorityQueueTest extends TestCase {
@@ -781,6 +785,42 @@
         }
     }
 
+    public void test_spliterator() throws Exception {
+        ArrayList<Integer> testElements = new ArrayList<>(
+                Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16));
+        PriorityQueue<Integer> list = new PriorityQueue<>();
+        list.addAll(testElements);
+
+        SpliteratorTester.runBasicIterationTests(list.spliterator(), testElements);
+        SpliteratorTester.runBasicSplitTests(list, testElements);
+        SpliteratorTester.testSpliteratorNPE(list.spliterator());
+
+        assertTrue(list.spliterator().hasCharacteristics(
+                Spliterator.SIZED | Spliterator.SUBSIZED));
+
+        SpliteratorTester.runSizedTests(list, 16 /* expected size */);
+        SpliteratorTester.runSubSizedTests(list, 16 /* expected size */);
+    }
+
+    public void test_spliterator_CME() throws Exception {
+        PriorityQueue<Integer> list = new PriorityQueue<>();
+        list.add(52);
+
+        Spliterator<Integer> sp = list.spliterator();
+        try {
+            sp.tryAdvance(value -> list.add(value));
+            fail();
+        } catch (ConcurrentModificationException expected) {
+        }
+
+        try {
+            sp.forEachRemaining(value -> list.add(value));
+            fail();
+        } catch (ConcurrentModificationException expected) {
+        }
+    }
+
+
     private static class MockComparator<E> implements Comparator<E> {
 
         public int compare(E object1, E object2) {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TreeSetTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TreeSetTest.java
index 43efa2d..8d1c3f8 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TreeSetTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TreeSetTest.java
@@ -17,12 +17,18 @@
 
 package org.apache.harmony.tests.java.util;
 
+import libcore.java.util.SpliteratorTester;
+
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Set;
 import java.util.SortedSet;
+import java.util.Spliterator;
 import java.util.TreeSet;
 
 public class TreeSetTest extends junit.framework.TestCase {
@@ -321,6 +327,25 @@
         assertFalse("Sets should not be equal 4", s2.equals(s1));
     }
 
+    public void test_spliterator() throws Exception {
+        TreeSet<String> treeSet = new TreeSet<>();
+        List<String> keys = Arrays.asList(
+                "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p");
+        treeSet.addAll(keys);
+
+        ArrayList<String> expectedKeys = new ArrayList<>(keys);
+        SpliteratorTester.runBasicIterationTests_unordered(treeSet.spliterator(), expectedKeys,
+                String::compareTo);
+        SpliteratorTester.runBasicSplitTests(treeSet, expectedKeys);
+        SpliteratorTester.testSpliteratorNPE(treeSet.spliterator());
+
+        assertTrue(treeSet.spliterator().hasCharacteristics(Spliterator.ORDERED));
+        SpliteratorTester.runOrderedTests(keys);
+
+        assertTrue(treeSet.spliterator().hasCharacteristics(Spliterator.DISTINCT));
+        SpliteratorTester.runDistinctTests(keys);
+    }
+
     /**
      * Sets up the fixture, for example, open a network connection. This method
      * is called before a test is executed.
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java
index 99d4434..0b56eec 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java
@@ -18,7 +18,10 @@
 package org.apache.harmony.tests.java.util;
 
 import libcore.java.util.ForEachRemainingTester;
+import libcore.java.util.SpliteratorTester;
 import tests.support.Support_ListTest;
+
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.ConcurrentModificationException;
@@ -27,6 +30,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.NoSuchElementException;
+import java.util.Spliterator;
 import java.util.Vector;
 
 public class VectorTest extends junit.framework.TestCase {
@@ -1424,6 +1428,41 @@
         ForEachRemainingTester.runTests(Vector.class, new String[] { "foo" });
     }
 
+    public void test_spliterator() throws Exception {
+        ArrayList<Integer> testElements = new ArrayList<>(
+                Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16));
+        Vector<Integer> list = new Vector<>();
+        list.addAll(testElements);
+
+        SpliteratorTester.runBasicIterationTests(list.spliterator(), testElements);
+        SpliteratorTester.runBasicSplitTests(list, testElements);
+        SpliteratorTester.testSpliteratorNPE(list.spliterator());
+
+        assertTrue(list.spliterator().hasCharacteristics(
+                Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED));
+
+        SpliteratorTester.runOrderedTests(list);
+        SpliteratorTester.runSizedTests(list, 16 /* expected size */);
+        SpliteratorTester.runSubSizedTests(list, 16 /* expected size */);
+    }
+
+    public void test_spliterator_CME() throws Exception {
+        Vector<Integer> list = new Vector<>();
+        list.add(52);
+
+        Spliterator<Integer> sp = list.spliterator();
+        try {
+            sp.tryAdvance(value -> list.add(value));
+            fail();
+        } catch (ConcurrentModificationException expected) {
+        }
+
+        try {
+            sp.forEachRemaining(value -> list.add(value));
+            fail();
+        } catch (ConcurrentModificationException expected) {
+        }
+    }
 
     /**
      * Sets up the fixture, for example, open a network connection. This method
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/WeakHashMapTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/WeakHashMapTest.java
index 1732dd3..302b505 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/WeakHashMapTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/WeakHashMapTest.java
@@ -21,15 +21,19 @@
 import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.ConcurrentModificationException;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.Spliterator;
 import java.util.WeakHashMap;
 import libcore.java.lang.ref.FinalizationTester;
 
+import libcore.java.util.SpliteratorTester;
 import tests.support.Support_MapTest2;
 
 public class WeakHashMapTest extends junit.framework.TestCase {
@@ -505,6 +509,101 @@
         assertEquals(1, processed.size());
     }
 
+    public void test_spliterator_keySet() {
+        WeakHashMap<String, String> hashMap = new WeakHashMap<>();
+        hashMap.put("a", "1");
+        hashMap.put("b", "2");
+        hashMap.put("c", "3");
+        hashMap.put("d", "4");
+        hashMap.put("e", "5");
+        hashMap.put("f", "6");
+        hashMap.put("g", "7");
+        hashMap.put("h", "8");
+        hashMap.put("i", "9");
+        hashMap.put("j", "10");
+        hashMap.put("k", "11");
+        hashMap.put("l", "12");
+        hashMap.put("m", "13");
+        hashMap.put("n", "14");
+        hashMap.put("o", "15");
+        hashMap.put("p", "16");
+
+        Set<String> keys = hashMap.keySet();
+        ArrayList<String> expectedKeys = new ArrayList<>(keys);
+
+        SpliteratorTester.runBasicIterationTests_unordered(keys.spliterator(), expectedKeys,
+                String::compareTo);
+        SpliteratorTester.runBasicSplitTests(keys, expectedKeys);
+        SpliteratorTester.testSpliteratorNPE(keys.spliterator());
+
+        assertTrue(keys.spliterator().hasCharacteristics(Spliterator.DISTINCT));
+
+        SpliteratorTester.runDistinctTests(keys);
+    }
+
+    public void test_spliterator_valueSet() {
+        WeakHashMap<String, String> hashMap = new WeakHashMap<>();
+        hashMap.put("a", "1");
+        hashMap.put("b", "2");
+        hashMap.put("c", "3");
+        hashMap.put("d", "4");
+        hashMap.put("e", "5");
+        hashMap.put("f", "6");
+        hashMap.put("g", "7");
+        hashMap.put("h", "8");
+        hashMap.put("i", "9");
+        hashMap.put("j", "10");
+        hashMap.put("k", "11");
+        hashMap.put("l", "12");
+        hashMap.put("m", "13");
+        hashMap.put("n", "14");
+        hashMap.put("o", "15");
+        hashMap.put("p", "16");
+
+        Collection<String> values = hashMap.values();
+        ArrayList<String> expectedValues = new ArrayList<>(values);
+
+        SpliteratorTester.runBasicIterationTests_unordered(
+                values.spliterator(), expectedValues, String::compareTo);
+        SpliteratorTester.runBasicSplitTests(values, expectedValues);
+        SpliteratorTester.testSpliteratorNPE(values.spliterator());
+    }
+
+    public void test_spliterator_entrySet() {
+        WeakHashMap<String, String> hashMap = new WeakHashMap<>();
+        hashMap.put("a", "1");
+        hashMap.put("b", "2");
+        hashMap.put("c", "3");
+        hashMap.put("d", "4");
+        hashMap.put("e", "5");
+        hashMap.put("f", "6");
+        hashMap.put("g", "7");
+        hashMap.put("h", "8");
+        hashMap.put("i", "9");
+        hashMap.put("j", "10");
+        hashMap.put("k", "11");
+        hashMap.put("l", "12");
+        hashMap.put("m", "13");
+        hashMap.put("n", "14");
+        hashMap.put("o", "15");
+        hashMap.put("p", "16");
+
+        Set<Map.Entry<String, String>> values = hashMap.entrySet();
+        ArrayList<Map.Entry<String, String>> expectedValues = new ArrayList<>(values);
+
+        Comparator<Map.Entry<String, String>> comparator =
+                (a, b) -> (a.getKey().compareTo(b.getKey()));
+
+        SpliteratorTester.runBasicIterationTests_unordered(values.spliterator(), expectedValues,
+                (a, b) -> (a.getKey().compareTo(b.getKey())));
+        SpliteratorTester.runBasicSplitTests(values, expectedValues, comparator);
+        SpliteratorTester.testSpliteratorNPE(values.spliterator());
+
+        assertTrue(values.spliterator().hasCharacteristics(Spliterator.DISTINCT));
+
+        SpliteratorTester.runDistinctTests(values);
+    }
+
     /**
      * Sets up the fixture, for example, open a network connection. This method
      * is called before a test is executed.
diff --git a/luni/src/test/java/libcore/java/util/SpliteratorTester.java b/luni/src/test/java/libcore/java/util/SpliteratorTester.java
new file mode 100644
index 0000000..a5b076b
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/SpliteratorTester.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * 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 libcore.java.util;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+public class SpliteratorTester {
+    public static <T> void runBasicIterationTests(Spliterator<T> spliterator,
+            ArrayList<T> expectedElements) {
+        ArrayList<T> recorder = new ArrayList<T>(expectedElements.size());
+        Consumer<T> consumer = (T value) -> recorder.add(value);
+
+        // tryAdvance.
+        assertTrue(spliterator.tryAdvance(consumer));
+        assertEquals(expectedElements.get(0), recorder.get(0));
+
+        // forEachRemaining.
+        spliterator.forEachRemaining(consumer);
+        assertEquals(expectedElements, recorder);
+
+        // There should be no more elements remaining in this spliterator.
+        assertFalse(spliterator.tryAdvance(consumer));
+        spliterator.forEachRemaining((T) -> fail());
+    }
+
+    public static <T> void runBasicIterationTests_unordered(Spliterator<T> spliterator,
+            ArrayList<T> expectedElements, Comparator<T> comparator) {
+        ArrayList<T> recorder = new ArrayList<T>(expectedElements.size());
+        Consumer<T> consumer = (T value) -> recorder.add(value);
+
+        // tryAdvance.
+        assertTrue(spliterator.tryAdvance(consumer));
+        assertTrue(expectedElements.contains(recorder.get(0)));
+
+        // forEachRemaining.
+        spliterator.forEachRemaining(consumer);
+        Collections.sort(expectedElements, comparator);
+        Collections.sort(recorder, comparator);
+        assertEquals(expectedElements, recorder);
+
+        // There should be no more elements remaining in this spliterator.
+        assertFalse(spliterator.tryAdvance(consumer));
+        spliterator.forEachRemaining((T) -> fail());
+    }
+
+    private static <T> void recordAndAssertBasicIteration(
+            Spliterator<T> spliterator, ArrayList<T> recorder) {
+        spliterator.tryAdvance(value -> recorder.add(value));
+        spliterator.forEachRemaining(value -> recorder.add(value));
+
+        // There shouldn't be any elements left in the spliterator.
+        assertFalse(spliterator.tryAdvance(value -> recorder.add(value)));
+        spliterator.tryAdvance(value -> fail());
+
+        // And all subsequent splits should fail.
+        assertNull(spliterator.trySplit());
+    }
+
+    public static void testSpliteratorNPE(Spliterator<?> spliterator) {
+        try {
+            spliterator.tryAdvance(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            spliterator.forEachRemaining(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    public static <T extends Comparable<T>> void runBasicSplitTests(
+            Iterable<T> spliterable, ArrayList<T> expectedElements) {
+        runBasicSplitTests(spliterable, expectedElements, T::compareTo);
+    }
+
+    public static <T> void runBasicSplitTests(Spliterator<T> spliterator,
+            ArrayList<T> expectedElements, Comparator<T> comparator) {
+        ArrayList<T> recorder = new ArrayList<>();
+
+        // Advance the original spliterator by one element.
+        assertTrue(spliterator.tryAdvance(value -> recorder.add(value)));
+
+        // Try splitting it.
+        Spliterator<T> split1 = spliterator.trySplit();
+        if (split1 != null) {
+            // Try to split the resulting split.
+            Spliterator<T> split1_1 = split1.trySplit();
+            Spliterator<T> split1_2 = split1.trySplit();
+            if (split1_1 != null) {
+                recordAndAssertBasicIteration(split1_1, recorder);
+            }
+            if (split1_2 != null) {
+                recordAndAssertBasicIteration(split1_2, recorder);
+            }
+
+            // Iterate over the remainder of split1.
+            recordAndAssertBasicIteration(split1, recorder);
+        }
+
+        // Try to split the original iterator again.
+        Spliterator<T> split2 = spliterator.trySplit();
+        if (split2 != null) {
+            recordAndAssertBasicIteration(split2, recorder);
+        }
+
+        // Record all remaining elements of the original spliterator.
+        recordAndAssertBasicIteration(spliterator, recorder);
+
+        Collections.sort(expectedElements, comparator);
+        Collections.sort(recorder, comparator);
+        assertEquals(expectedElements, recorder);
+    }
+
+    /**
+     * Note that the contract of trySplit() is generally quite weak (as it must be). There
+     * are no demands about when the spliterator can or cannot split itself. In general, this
+     * test is quite loose. All it does is exercise the basic methods on the splits (if any)
+     * and confirms that the union of all elements in the split is the collection that was
+     * iterated over.
+     */
+    public static <T> void runBasicSplitTests(Iterable<T> spliterable,
+            ArrayList<T> expectedElements, Comparator<T> comparator) {
+        runBasicSplitTests(spliterable.spliterator(), expectedElements, comparator);
+    }
+
+    public static <T> void runOrderedTests(Iterable<T> spliterable) {
+        ArrayList<T> iteration1 = new ArrayList<>();
+        ArrayList<T> iteration2 = new ArrayList<>();
+
+        spliterable.spliterator().forEachRemaining(value -> iteration1.add(value));
+        spliterable.spliterator().forEachRemaining(value -> iteration2.add(value));
+
+        assertEquals(iteration1, iteration2);
+
+        iteration1.clear();
+        iteration2.clear();
+
+        spliterable.spliterator().trySplit().forEachRemaining(value -> iteration1.add(value));
+        spliterable.spliterator().trySplit().forEachRemaining(value -> iteration2.add(value));
+        assertEquals(iteration1, iteration2);
+    }
+
+    public static <T> void runSizedTests(Spliterator<T> spliterator, int expectedSize) {
+        assertEquals(expectedSize, spliterator.estimateSize());
+        assertEquals(expectedSize, spliterator.getExactSizeIfKnown());
+    }
+
+    public static <T> void runSizedTests(Iterable<T> spliterable, int expectedSize) {
+        runSizedTests(spliterable.spliterator(), expectedSize);
+    }
+
+    public static <T> void runSubSizedTests(Spliterator<T> spliterator, int expectedSize) {
+        assertEquals(expectedSize, spliterator.estimateSize());
+        assertEquals(expectedSize, spliterator.getExactSizeIfKnown());
+
+
+        Spliterator<T> split1 = spliterator.trySplit();
+        assertEquals(expectedSize, spliterator.estimateSize() + split1.estimateSize());
+        assertEquals(expectedSize, spliterator.getExactSizeIfKnown() + split1.getExactSizeIfKnown());
+    }
+
+    public static <T> void runSubSizedTests(Iterable<T> spliterable, int expectedSize) {
+        runSubSizedTests(spliterable.spliterator(), expectedSize);
+    }
+
+    public static <T> void runDistinctTests(Iterable<T> spliterable) {
+        HashSet<T> distinct = new HashSet<>();
+        ArrayList<T> allElements = new ArrayList<>();
+
+        Spliterator<T> spliterator = spliterable.spliterator();
+        Spliterator<T> split1 = spliterator.trySplit();
+
+        // First test that iterating via the spliterator using forEachRemaining
+        // yields distinct elements.
+        spliterator.forEachRemaining(value -> { distinct.add(value); allElements.add(value); });
+        split1.forEachRemaining(value -> { distinct.add(value); allElements.add(value); });
+        assertEquals(distinct.size(), allElements.size());
+
+        distinct.clear();
+        allElements.clear();
+        spliterator = spliterable.spliterator();
+        split1 = spliterator.trySplit();
+
+        // Then test whether using tryAdvance yields the same results.
+        while (spliterator.tryAdvance(value -> { distinct.add(value); allElements.add(value); })) {
+        }
+
+        while (split1.tryAdvance(value -> { distinct.add(value); allElements.add(value); })) {
+        }
+
+        assertEquals(distinct.size(), allElements.size());
+    }
+
+    public static <T> void runSortedTests(Iterable<T> spliterable, Comparator<T> comparator) {
+        Spliterator<T> spliterator = spliterable.spliterator();
+        Spliterator<T> split1 = spliterator.trySplit();
+
+        ArrayList<T> elements = new ArrayList<>();
+        spliterator.forEachRemaining(value -> elements.add(value));
+
+        ArrayList<T> sortedElements = new ArrayList<>(elements);
+        Collections.sort(sortedElements, comparator);
+        assertEquals(elements, sortedElements);
+
+        elements.clear();
+
+        split1.forEachRemaining(value -> elements.add(value));
+        sortedElements = new ArrayList<>(elements);
+        Collections.sort(sortedElements, comparator);
+        assertEquals(elements, sortedElements);
+    }
+
+    public static <T extends Comparable<T>> void runSortedTests(Iterable<T> spliterable) {
+        runSortedTests(spliterable, T::compareTo);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/SpliteratorsTest.java b/luni/src/test/java/libcore/java/util/SpliteratorsTest.java
new file mode 100644
index 0000000..639ebf1
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/SpliteratorsTest.java
@@ -0,0 +1,866 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * 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 libcore.java.util;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.PrimitiveIterator;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+public class SpliteratorsTest extends TestCase {
+
+    public void testEmptyIntSpliterator() {
+        Spliterator.OfInt empty = Spliterators.emptyIntSpliterator();
+        assertNull(empty.trySplit());
+        assertEquals(0, empty.estimateSize());
+        assertEquals(0, empty.getExactSizeIfKnown());
+
+        IntConsumer alwaysFails = (int value) -> fail();
+        Consumer<Integer> alwaysFailsBoxed = (Integer value) -> fail();
+        empty.tryAdvance(alwaysFails);
+        empty.tryAdvance(alwaysFailsBoxed);
+
+        empty.forEachRemaining(alwaysFails);
+        empty.forEachRemaining(alwaysFailsBoxed);
+
+        assertEquals(Spliterator.SIZED | Spliterator.SUBSIZED, empty.characteristics());
+    }
+
+    public void testEmptyRefSpliterator() {
+        Spliterator<Object> empty = Spliterators.emptySpliterator();
+        assertNull(empty.trySplit());
+        assertEquals(0, empty.estimateSize());
+        assertEquals(0, empty.getExactSizeIfKnown());
+
+        Consumer<Object> alwaysFails = (Object value) -> fail();
+        empty.tryAdvance(alwaysFails);
+        empty.forEachRemaining(alwaysFails);
+
+        assertEquals(Spliterator.SIZED | Spliterator.SUBSIZED, empty.characteristics());
+    }
+
+    public void testEmptyLongSpliterator() {
+        Spliterator.OfLong empty = Spliterators.emptyLongSpliterator();
+        assertNull(empty.trySplit());
+        assertEquals(0, empty.estimateSize());
+        assertEquals(0, empty.getExactSizeIfKnown());
+
+        LongConsumer alwaysFails = (long value) -> fail();
+        Consumer<Long> alwaysFailsBoxed = (Long value) -> fail();
+        empty.tryAdvance(alwaysFails);
+        empty.tryAdvance(alwaysFailsBoxed);
+
+        empty.forEachRemaining(alwaysFails);
+        empty.forEachRemaining(alwaysFailsBoxed);
+
+        assertEquals(Spliterator.SIZED | Spliterator.SUBSIZED, empty.characteristics());
+    }
+
+    public void testEmptyDoubleSpliterator() {
+        Spliterator.OfDouble empty = Spliterators.emptyDoubleSpliterator();
+        assertNull(empty.trySplit());
+        assertEquals(0, empty.estimateSize());
+        assertEquals(0, empty.getExactSizeIfKnown());
+
+        DoubleConsumer alwaysFails = (double value) -> fail();
+        Consumer<Double> alwaysFailsBoxed = (Double value) -> fail();
+        empty.tryAdvance(alwaysFails);
+        empty.tryAdvance(alwaysFailsBoxed);
+
+        empty.forEachRemaining(alwaysFails);
+        empty.forEachRemaining(alwaysFailsBoxed);
+
+        assertEquals(Spliterator.SIZED | Spliterator.SUBSIZED, empty.characteristics());
+    }
+
+    public void testSpliteratorObjectArray() {
+        String[] array = { "a", "b", "c", "d", "e", "f", "g", "h" };
+        ArrayList<String> expectedValues = new ArrayList<>(Arrays.asList(array));
+
+        Spliterator<String> sp = Spliterators.spliterator(array, 0);
+        assertEquals(8, sp.estimateSize());
+        assertEquals(8, sp.getExactSizeIfKnown());
+
+        sp = Spliterators.spliterator(array, 0);
+        SpliteratorTester.runBasicIterationTests(sp, expectedValues);
+
+        sp = Spliterators.spliterator(array, 0);
+        SpliteratorTester.testSpliteratorNPE(sp);
+
+        sp = Spliterators.spliterator(array, 0);
+        SpliteratorTester.runBasicSplitTests(sp, expectedValues, String::compareTo);
+
+        sp = Spliterators.spliterator(array, 0);
+        SpliteratorTester.runSizedTests(sp, 8);
+
+        sp = Spliterators.spliterator(array, 0);
+        SpliteratorTester.runSubSizedTests(sp, 8);
+
+        // Assert the spliterator inherits any characteristics we ask it to.
+        sp = Spliterators.spliterator(array, Spliterator.ORDERED);
+        assertTrue(sp.hasCharacteristics(Spliterator.ORDERED));
+    }
+
+    public void testSpliteratorObjectArrayRange() {
+        String[] array = { "FOO", "BAR", "a", "b", "c", "d", "e", "f", "g", "h", "BAZ", "BAH" };
+        ArrayList<String> expectedValues = new ArrayList<>(
+                Arrays.asList(Arrays.copyOfRange(array, 2, 10)));
+
+        Spliterator<String> sp = Spliterators.spliterator(array, 2, 10, 0);
+        assertEquals(8, sp.estimateSize());
+        assertEquals(8, sp.getExactSizeIfKnown());
+
+        sp = Spliterators.spliterator(array, 2, 10, 0);
+        SpliteratorTester.runBasicIterationTests(sp, expectedValues);
+
+        sp = Spliterators.spliterator(array, 2, 10, 0);
+        SpliteratorTester.testSpliteratorNPE(sp);
+
+        sp = Spliterators.spliterator(array, 2, 10, 0);
+        SpliteratorTester.runBasicSplitTests(sp, expectedValues, String::compareTo);
+
+        sp = Spliterators.spliterator(array, 2, 10, 0);
+        SpliteratorTester.runSizedTests(sp, 8);
+
+        sp = Spliterators.spliterator(array, 2, 10, 0);
+        SpliteratorTester.runSubSizedTests(sp, 8);
+
+        // Assert the spliterator inherits any characteristics we ask it to.
+        sp = Spliterators.spliterator(array, 2, 10, Spliterator.ORDERED);
+        assertTrue(sp.hasCharacteristics(Spliterator.ORDERED));
+    }
+
+    private static class PrimitiveIntArrayList {
+        final int[] array;
+        int idx;
+
+        PrimitiveIntArrayList(int size) {
+            array = new int[size];
+        }
+
+        public void add(int element) {
+            array[idx++] = element;
+        }
+
+        public int[] toSortedArray() {
+            Arrays.sort(array);
+            return array;
+        }
+    }
+
+    private static class PrimitiveLongArrayList {
+        final long[] array;
+        int idx;
+
+        PrimitiveLongArrayList(int size) {
+            array = new long[size];
+        }
+
+        public void add(long element) {
+            array[idx++] = element;
+        }
+
+        public long[] toSortedArray() {
+            Arrays.sort(array);
+            return array;
+        }
+    }
+
+    private static class PrimitiveDoubleArrayList {
+        final double[] array;
+        int idx;
+
+        PrimitiveDoubleArrayList(int size) {
+            array = new double[size];
+        }
+
+        public void add(double element) {
+            array[idx++] = element;
+        }
+
+        public double[] toSortedArray() {
+            Arrays.sort(array);
+            return array;
+        }
+    }
+
+    public void test_spliterator_int() {
+        int[] elements = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+
+        Spliterator.OfInt intSp = Spliterators.spliterator(elements, 0);
+
+        assertEquals(16, intSp.estimateSize());
+        assertEquals(16, intSp.getExactSizeIfKnown());
+
+        assertTrue(intSp.tryAdvance((Integer value) -> assertEquals(1, (int) value)));
+        assertTrue(intSp.tryAdvance((int value) -> assertEquals(2, (int) value)));
+
+        PrimitiveIntArrayList recorder = new PrimitiveIntArrayList(16);
+        // Record elements observed by previous tests.
+        recorder.add(1);
+        recorder.add(2);
+
+        Spliterator.OfInt split1 = intSp.trySplit();
+        assertNotNull(split1);
+        assertTrue(split1.tryAdvance((int value) -> recorder.add(value)));
+        assertTrue(split1.tryAdvance((Integer value) -> recorder.add(value)));
+
+        // Assert that splits can themselves resplit.
+        Spliterator.OfInt split2 = split1.trySplit();
+        assertNotNull(split2);
+        split2.forEachRemaining((int value) -> recorder.add(value));
+        assertFalse(split2.tryAdvance((int value) -> fail()));
+        assertFalse(split2.tryAdvance((Integer value) -> fail()));
+
+        // Iterate over the remaning elements so we can make sure we've looked at
+        // everything.
+        split1.forEachRemaining((int value) -> recorder.add(value));
+        intSp.forEachRemaining((int value) -> recorder.add(value));
+
+        int[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(elements), Arrays.toString(recorded));
+    }
+
+    public void test_spliterator_intOffsetBasic() {
+        int[] elements = { 123123, 131321312, 1, 2, 3, 4, 32323232, 45454};
+        Spliterator.OfInt sp = Spliterators.spliterator(elements, 2, 6, 0);
+
+        PrimitiveIntArrayList recorder = new PrimitiveIntArrayList(4);
+        sp.tryAdvance((Integer value) -> recorder.add((int) value));
+        sp.tryAdvance((int value) -> recorder.add(value));
+        sp.forEachRemaining((int value) -> recorder.add(value));
+
+        int[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(new int[] { 1, 2, 3, 4 }), Arrays.toString(recorded));
+    }
+
+    public void test_spliterator_longOffsetBasic() {
+        long[] elements = { 123123, 131321312, 1, 2, 3, 4, 32323232, 45454};
+        Spliterator.OfLong sp = Spliterators.spliterator(elements, 2, 6, 0);
+
+        PrimitiveLongArrayList recorder = new PrimitiveLongArrayList(4);
+        sp.tryAdvance((Long value) -> recorder.add((long) value));
+        sp.tryAdvance((long value) -> recorder.add(value));
+        sp.forEachRemaining((long value) -> recorder.add(value));
+
+        long[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(new long[] { 1, 2, 3, 4 }), Arrays.toString(recorded));
+    }
+
+    public void test_spliterator_doubleOffsetBasic() {
+        double[] elements = { 123123, 131321312, 1, 2, 3, 4, 32323232, 45454};
+        Spliterator.OfDouble sp = Spliterators.spliterator(elements, 2, 6, 0);
+
+        PrimitiveDoubleArrayList recorder = new PrimitiveDoubleArrayList(4);
+        sp.tryAdvance((Double value) -> recorder.add((double) value));
+        sp.tryAdvance((double value) -> recorder.add(value));
+        sp.forEachRemaining((double value) -> recorder.add(value));
+
+        double[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(new double[] { 1, 2, 3, 4 }), Arrays.toString(recorded));
+    }
+
+    public void test_spliterator_long() {
+        long[] elements = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+
+        Spliterator.OfLong longSp = Spliterators.spliterator(elements, 0);
+
+        assertEquals(16, longSp.estimateSize());
+        assertEquals(16, longSp.getExactSizeIfKnown());
+
+        assertTrue(longSp.tryAdvance((Long value) -> assertEquals(1, (long) value)));
+        assertTrue(longSp.tryAdvance((long value) -> assertEquals(2, (long) value)));
+
+        PrimitiveLongArrayList recorder = new PrimitiveLongArrayList(16);
+        // Record elements observed by previous tests.
+        recorder.add(1);
+        recorder.add(2);
+
+        Spliterator.OfLong split1 = longSp.trySplit();
+        assertNotNull(split1);
+        assertTrue(split1.tryAdvance((long value) -> recorder.add(value)));
+        assertTrue(split1.tryAdvance((Long value) -> recorder.add(value)));
+
+        // Assert that splits can themselves resplit.
+        Spliterator.OfLong split2 = split1.trySplit();
+        assertNotNull(split2);
+        split2.forEachRemaining((long value) -> recorder.add(value));
+        assertFalse(split2.tryAdvance((long value) -> fail()));
+        assertFalse(split2.tryAdvance((Long value) -> fail()));
+
+        // Iterate over the remaning elements so we can make sure we've looked at
+        // everything.
+        split1.forEachRemaining((long value) -> recorder.add(value));
+        longSp.forEachRemaining((long value) -> recorder.add(value));
+
+        long[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(elements), Arrays.toString(recorded));
+    }
+
+    public void test_spliterator_double() {
+        double[] elements = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+
+        Spliterator.OfDouble doubleSp = Spliterators.spliterator(elements, 0);
+
+        assertEquals(16, doubleSp.estimateSize());
+        assertEquals(16, doubleSp.getExactSizeIfKnown());
+
+        assertTrue(doubleSp.tryAdvance((Double value) -> assertEquals(1.0, (double) value)));
+        assertTrue(doubleSp.tryAdvance((double value) -> assertEquals(2.0, (double) value)));
+
+        PrimitiveDoubleArrayList recorder = new PrimitiveDoubleArrayList(16);
+        // Record elements observed by previous tests.
+        recorder.add(1);
+        recorder.add(2);
+
+        Spliterator.OfDouble split1 = doubleSp.trySplit();
+        assertNotNull(split1);
+        assertTrue(split1.tryAdvance((double value) -> recorder.add(value)));
+        assertTrue(split1.tryAdvance((Double value) -> recorder.add(value)));
+
+        // Assert that splits can themselves resplit.
+        Spliterator.OfDouble split2 = split1.trySplit();
+        assertNotNull(split2);
+        split2.forEachRemaining((double value) -> recorder.add(value));
+        assertFalse(split2.tryAdvance((double value) -> fail()));
+        assertFalse(split2.tryAdvance((Double value) -> fail()));
+
+        // Iterate over the remaining elements so we can make sure we've looked at
+        // everything.
+        split1.forEachRemaining((double value) -> recorder.add(value));
+        doubleSp.forEachRemaining((double value) -> recorder.add(value));
+
+        double[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(elements), Arrays.toString(recorded));
+    }
+
+    public void test_primitive_spliterators_NPE() {
+        final int[] elements = { 1, 2, 3, 4, 5, 6};
+        Spliterator.OfInt intSp = Spliterators.spliterator(elements, 0);
+        try {
+            intSp.forEachRemaining((Consumer<Integer>) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            assertTrue(intSp.tryAdvance((Consumer<Integer>) null));
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            intSp.forEachRemaining((IntConsumer) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            intSp.tryAdvance((IntConsumer) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        final long[] longElements = { 1, 2, 3, 4, 5, 6};
+        Spliterator.OfLong longSp = Spliterators.spliterator(longElements, 0);
+        try {
+            longSp.forEachRemaining((Consumer<Long>) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            longSp.tryAdvance((Consumer<Long>) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            longSp.forEachRemaining((LongConsumer) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            longSp.tryAdvance((LongConsumer) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        final double[] doubleElements = { 1, 2, 3, 4, 5, 6};
+        Spliterator.OfDouble doubleSp = Spliterators.spliterator(doubleElements, 0);
+        try {
+            doubleSp.forEachRemaining((Consumer<Double>) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            doubleSp.tryAdvance((Consumer<Double>) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            doubleSp.forEachRemaining((DoubleConsumer) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            doubleSp.tryAdvance((DoubleConsumer) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    public void testSpliterator_Collection() {
+        String[] array = { "a", "b", "c", "d", "e", "f", "g", "h" };
+        ArrayList<String> expectedValues = new ArrayList<>(Arrays.asList(array));
+
+        Collection<String> collection = Arrays.asList(array);
+
+        Spliterator<String> sp = Spliterators.spliterator(collection, 0);
+        assertEquals(8, sp.estimateSize());
+        assertEquals(8, sp.getExactSizeIfKnown());
+
+        sp = Spliterators.spliterator(collection, 0);
+        SpliteratorTester.runBasicIterationTests(sp, expectedValues);
+
+        sp = Spliterators.spliterator(collection, 0);
+        SpliteratorTester.testSpliteratorNPE(sp);
+
+        sp = Spliterators.spliterator(collection, 0);
+        SpliteratorTester.runBasicSplitTests(sp, expectedValues, String::compareTo);
+
+        sp = Spliterators.spliterator(collection, 0);
+        SpliteratorTester.runSizedTests(sp, 8);
+
+        sp = Spliterators.spliterator(collection, 0);
+        SpliteratorTester.runSubSizedTests(sp, 8);
+
+        // Assert the spliterator inherits any characteristics we ask it to.
+        sp = Spliterators.spliterator(collection, Spliterator.ORDERED);
+        assertTrue(sp.hasCharacteristics(Spliterator.ORDERED));
+    }
+
+    public void testSpliterator_sizedIterator() {
+        String[] array = { "a", "b", "c", "d", "e", "f", "g", "h" };
+        List<String> asList = Arrays.asList(array);
+        ArrayList<String> expectedValues = new ArrayList<>(asList);
+
+        Spliterator<String> sp = Spliterators.spliterator(asList.iterator(), 8 /* size */, 0);
+        assertEquals(8, sp.estimateSize());
+        assertEquals(8, sp.getExactSizeIfKnown());
+
+        sp = Spliterators.spliterator(asList.iterator(), 8 /* size */, 0);
+        SpliteratorTester.runBasicIterationTests(sp, expectedValues);
+
+        sp = Spliterators.spliterator(asList.iterator(), 8 /* size */, 0);
+        SpliteratorTester.testSpliteratorNPE(sp);
+
+        sp = Spliterators.spliterator(asList.iterator(), 8 /* size */, 0);
+        SpliteratorTester.runBasicSplitTests(sp, expectedValues, String::compareTo);
+
+        sp = Spliterators.spliterator(asList.iterator(), 8 /* size */, 0);
+        SpliteratorTester.runSizedTests(sp, 8);
+
+        sp = Spliterators.spliterator(asList.iterator(), 8 /* size */, 0);
+        SpliteratorTester.runSubSizedTests(sp, 8);
+
+        // Assert the spliterator inherits any characteristics we ask it to.
+        sp = Spliterators.spliterator(array, Spliterator.ORDERED);
+        assertTrue(sp.hasCharacteristics(Spliterator.ORDERED));
+    }
+
+    public void testSpliterator_unsizedIterator() {
+        String[] array = { "a", "b", "c", "d", "e", "f", "g", "h" };
+        List<String> asList = Arrays.asList(array);
+        ArrayList<String> expectedValues = new ArrayList<>(asList);
+
+        Spliterator<String> sp = Spliterators.spliteratorUnknownSize(asList.iterator(), 0);
+        SpliteratorTester.runBasicIterationTests(sp, expectedValues);
+
+        sp = Spliterators.spliteratorUnknownSize(asList.iterator(), 0);
+        SpliteratorTester.testSpliteratorNPE(sp);
+
+        sp = Spliterators.spliteratorUnknownSize(asList.iterator(), 0);
+        SpliteratorTester.runBasicSplitTests(sp, expectedValues, String::compareTo);
+
+        // Assert the spliterator inherits any characteristics we ask it to.
+        sp = Spliterators.spliterator(array, Spliterator.ORDERED);
+        assertTrue(sp.hasCharacteristics(Spliterator.ORDERED));
+    }
+
+    private static class CannedIntPrimitiveIterator implements PrimitiveIterator.OfInt {
+        private final int[] data;
+        private int idx;
+
+        public CannedIntPrimitiveIterator(int[] data) {
+            this.data = data;
+            this.idx = 0;
+        }
+
+        @Override
+        public int nextInt() {
+            return data[idx++];
+        }
+
+        @Override
+        public boolean hasNext() {
+            return idx < data.length;
+        }
+    }
+
+    private static class CannedLongPrimitiveIterator implements PrimitiveIterator.OfLong {
+        private final long[] data;
+        private int idx;
+
+        public CannedLongPrimitiveIterator(long[] data) {
+            this.data = data;
+            this.idx = 0;
+        }
+
+        @Override
+        public long nextLong() {
+            return data[idx++];
+        }
+
+        @Override
+        public boolean hasNext() {
+            return idx < data.length;
+        }
+    }
+
+    private static class CannedDoublePrimitiveIterator implements PrimitiveIterator.OfDouble {
+        private final double[] data;
+        private int idx;
+
+        public CannedDoublePrimitiveIterator(double[] data) {
+            this.data = data;
+            this.idx = 0;
+        }
+
+        @Override
+        public double nextDouble() {
+            return data[idx++];
+        }
+
+        @Override
+        public boolean hasNext() {
+            return idx < data.length;
+        }
+
+
+    }
+
+    public void test_spliterator_intPrimitiveIterator() {
+        int[] data = new int[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+        PrimitiveIterator.OfInt elements = new CannedIntPrimitiveIterator(data);
+        Spliterator.OfInt intSp = Spliterators.spliterator(elements, 16 /* size */, 0);
+
+        assertEquals(16, intSp.estimateSize());
+        assertEquals(16, intSp.getExactSizeIfKnown());
+
+        assertTrue(intSp.tryAdvance((Integer value) -> assertEquals(1, (int) value)));
+        assertTrue(intSp.tryAdvance((int value) -> assertEquals(2, (int) value)));
+
+        PrimitiveIntArrayList recorder = new PrimitiveIntArrayList(16);
+        // Record elements observed by previous tests.
+        recorder.add(1);
+        recorder.add(2);
+
+        Spliterator.OfInt split1 = intSp.trySplit();
+        assertNotNull(split1);
+        assertTrue(split1.tryAdvance((int value) -> recorder.add(value)));
+        assertTrue(split1.tryAdvance((Integer value) -> recorder.add(value)));
+
+        // Assert that splits can themselves resplit.
+        Spliterator.OfInt split2 = split1.trySplit();
+        assertNotNull(split2);
+        split2.forEachRemaining((int value) -> recorder.add(value));
+        assertFalse(split2.tryAdvance((int value) -> fail()));
+        assertFalse(split2.tryAdvance((Integer value) -> fail()));
+
+        // Iterate over the remaning elements so we can make sure we've looked at
+        // everything.
+        split1.forEachRemaining((int value) -> recorder.add(value));
+        intSp.forEachRemaining((int value) -> recorder.add(value));
+
+        int[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(data), Arrays.toString(recorded));
+    }
+
+    public void test_spliterator_intPrimitiveIterator_unsized() {
+        int[] data = new int[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+        PrimitiveIterator.OfInt elements = new CannedIntPrimitiveIterator(data);
+        Spliterator.OfInt intSp = Spliterators.spliteratorUnknownSize(elements, 0);
+
+        assertTrue(intSp.tryAdvance((Integer value) -> assertEquals(1, (int) value)));
+        assertTrue(intSp.tryAdvance((int value) -> assertEquals(2, (int) value)));
+
+        PrimitiveIntArrayList recorder = new PrimitiveIntArrayList(16);
+        // Record elements observed by previous tests.
+        recorder.add(1);
+        recorder.add(2);
+
+        Spliterator.OfInt split1 = intSp.trySplit();
+        assertNotNull(split1);
+        assertTrue(split1.tryAdvance((int value) -> recorder.add(value)));
+        assertTrue(split1.tryAdvance((Integer value) -> recorder.add(value)));
+
+        // Assert that splits can themselves resplit.
+        Spliterator.OfInt split2 = split1.trySplit();
+        assertNotNull(split2);
+        split2.forEachRemaining((int value) -> recorder.add(value));
+        assertFalse(split2.tryAdvance((int value) -> fail()));
+        assertFalse(split2.tryAdvance((Integer value) -> fail()));
+
+        // Iterate over the remaning elements so we can make sure we've looked at
+        // everything.
+        split1.forEachRemaining((int value) -> recorder.add(value));
+        intSp.forEachRemaining((int value) -> recorder.add(value));
+
+        int[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(data), Arrays.toString(recorded));
+    }
+
+    public void test_spliterator_longPrimitiveIterator() {
+        long[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+        PrimitiveIterator.OfLong elements = new CannedLongPrimitiveIterator(data);
+        Spliterator.OfLong longSp = Spliterators.spliterator(elements, 16 /* size */, 0);
+
+        assertEquals(16, longSp.estimateSize());
+        assertEquals(16, longSp.getExactSizeIfKnown());
+
+        assertTrue(longSp.tryAdvance((Long value) -> assertEquals(1, (long) value)));
+        assertTrue(longSp.tryAdvance((long value) -> assertEquals(2, (long) value)));
+
+        PrimitiveLongArrayList recorder = new PrimitiveLongArrayList(16);
+        // Record elements observed by previous tests.
+        recorder.add(1);
+        recorder.add(2);
+
+        Spliterator.OfLong split1 = longSp.trySplit();
+        assertNotNull(split1);
+        assertTrue(split1.tryAdvance((long value) -> recorder.add(value)));
+        assertTrue(split1.tryAdvance((Long value) -> recorder.add(value)));
+
+        // Assert that splits can themselves resplit.
+        Spliterator.OfLong split2 = split1.trySplit();
+        assertNotNull(split2);
+        split2.forEachRemaining((long value) -> recorder.add(value));
+        assertFalse(split2.tryAdvance((long value) -> fail()));
+        assertFalse(split2.tryAdvance((Long value) -> fail()));
+
+        // Iterate over the remaning elements so we can make sure we've looked at
+        // everything.
+        split1.forEachRemaining((long value) -> recorder.add(value));
+        longSp.forEachRemaining((long value) -> recorder.add(value));
+
+        long[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(data), Arrays.toString(recorded));
+    }
+
+    public void test_spliterator_longPrimitiveIterator_unsized() {
+        long[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+        PrimitiveIterator.OfLong elements = new CannedLongPrimitiveIterator(data);
+        Spliterator.OfLong longSp = Spliterators.spliteratorUnknownSize(elements, 0);
+
+        assertTrue(longSp.tryAdvance((Long value) -> assertEquals(1, (long) value)));
+        assertTrue(longSp.tryAdvance((long value) -> assertEquals(2, (long) value)));
+
+        PrimitiveLongArrayList recorder = new PrimitiveLongArrayList(16);
+        // Record elements observed by previous tests.
+        recorder.add(1);
+        recorder.add(2);
+
+        Spliterator.OfLong split1 = longSp.trySplit();
+        assertNotNull(split1);
+        assertTrue(split1.tryAdvance((long value) -> recorder.add(value)));
+        assertTrue(split1.tryAdvance((Long value) -> recorder.add(value)));
+
+        // Assert that splits can themselves resplit.
+        Spliterator.OfLong split2 = split1.trySplit();
+        assertNotNull(split2);
+        split2.forEachRemaining((long value) -> recorder.add(value));
+        assertFalse(split2.tryAdvance((long value) -> fail()));
+        assertFalse(split2.tryAdvance((Long value) -> fail()));
+
+        // Iterate over the remaning elements so we can make sure we've looked at
+        // everything.
+        split1.forEachRemaining((long value) -> recorder.add(value));
+        longSp.forEachRemaining((long value) -> recorder.add(value));
+
+        long[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(data), Arrays.toString(recorded));
+    }
+
+    public void test_spliterator_doublePrimitiveIterator() {
+        double[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+        PrimitiveIterator.OfDouble elements = new CannedDoublePrimitiveIterator(data);
+        Spliterator.OfDouble doubleSp = Spliterators.spliterator(elements, 16 /* size */, 0);
+
+        assertEquals(16, doubleSp.estimateSize());
+        assertEquals(16, doubleSp.getExactSizeIfKnown());
+
+        assertTrue(doubleSp.tryAdvance((Double value) -> assertEquals(1.0, (double) value)));
+        assertTrue(doubleSp.tryAdvance((double value) -> assertEquals(2.0, (double) value)));
+
+        PrimitiveDoubleArrayList recorder = new PrimitiveDoubleArrayList(16);
+        // Record elements observed by previous tests.
+        recorder.add(1);
+        recorder.add(2);
+
+        Spliterator.OfDouble split1 = doubleSp.trySplit();
+        assertNotNull(split1);
+        assertTrue(split1.tryAdvance((double value) -> recorder.add(value)));
+        assertTrue(split1.tryAdvance((Double value) -> recorder.add(value)));
+
+        // Assert that splits can themselves resplit.
+        Spliterator.OfDouble split2 = split1.trySplit();
+        assertNotNull(split2);
+        split2.forEachRemaining((double value) -> recorder.add(value));
+        assertFalse(split2.tryAdvance((double value) -> fail()));
+        assertFalse(split2.tryAdvance((Double value) -> fail()));
+
+        // Iterate over the remaining elements so we can make sure we've looked at
+        // everything.
+        split1.forEachRemaining((double value) -> recorder.add(value));
+        doubleSp.forEachRemaining((double value) -> recorder.add(value));
+
+        double[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(data), Arrays.toString(recorded));
+    }
+
+    public void test_spliterator_doublePrimitiveIterator_unsized() {
+        double[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+        PrimitiveIterator.OfDouble elements = new CannedDoublePrimitiveIterator(data);
+        Spliterator.OfDouble doubleSp = Spliterators.spliteratorUnknownSize(elements, 0);
+
+        assertTrue(doubleSp.tryAdvance((Double value) -> assertEquals(1.0, (double) value)));
+        assertTrue(doubleSp.tryAdvance((double value) -> assertEquals(2.0, (double) value)));
+
+        PrimitiveDoubleArrayList recorder = new PrimitiveDoubleArrayList(16);
+        // Record elements observed by previous tests.
+        recorder.add(1);
+        recorder.add(2);
+
+        Spliterator.OfDouble split1 = doubleSp.trySplit();
+        assertNotNull(split1);
+        assertTrue(split1.tryAdvance((double value) -> recorder.add(value)));
+        assertTrue(split1.tryAdvance((Double value) -> recorder.add(value)));
+
+        // Assert that splits can themselves resplit.
+        Spliterator.OfDouble split2 = split1.trySplit();
+        assertNotNull(split2);
+        split2.forEachRemaining((double value) -> recorder.add(value));
+        assertFalse(split2.tryAdvance((double value) -> fail()));
+        assertFalse(split2.tryAdvance((Double value) -> fail()));
+
+        // Iterate over the remaining elements so we can make sure we've looked at
+        // everything.
+        split1.forEachRemaining((double value) -> recorder.add(value));
+        doubleSp.forEachRemaining((double value) -> recorder.add(value));
+
+        double[] recorded = recorder.toSortedArray();
+        assertEquals(Arrays.toString(data), Arrays.toString(recorded));
+    }
+
+    // Conversions from spliterators to iterators
+
+    public void test_iterator_int() {
+        int[] data = new int[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+        Spliterator.OfInt intSp = Spliterators.spliterator(data, 0);
+        PrimitiveIterator.OfInt it = Spliterators.iterator(intSp);
+
+        assertTrue(it.hasNext());
+        assertEquals(1, (int) it.next());
+        assertEquals(2, it.nextInt());
+
+        PrimitiveIntArrayList recorder = new PrimitiveIntArrayList(16);
+        recorder.add(1);
+        recorder.add(2);
+        it.forEachRemaining((Integer l) -> recorder.add(l));
+        assertEquals(Arrays.toString(data), Arrays.toString(recorder.toSortedArray()));
+
+        assertFalse(it.hasNext());
+    }
+
+    public void test_iterator_long() {
+        long[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+        Spliterator.OfLong longSp = Spliterators.spliterator(data, 0);
+        PrimitiveIterator.OfLong it = Spliterators.iterator(longSp);
+
+        assertTrue(it.hasNext());
+        assertEquals(1, (long) it.next());
+        assertEquals(2, it.nextLong());
+
+        PrimitiveLongArrayList recorder = new PrimitiveLongArrayList(16);
+        recorder.add(1);
+        recorder.add(2);
+        it.forEachRemaining((Long l) -> recorder.add(l));
+        assertEquals(Arrays.toString(data), Arrays.toString(recorder.toSortedArray()));
+
+        assertFalse(it.hasNext());
+    }
+
+    public void test_iterator_double() {
+        double[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+        Spliterator.OfDouble doubleSp = Spliterators.spliterator(data, 0);
+
+        PrimitiveIterator.OfDouble it = Spliterators.iterator(doubleSp);
+
+        assertTrue(it.hasNext());
+        assertEquals(1.0, it.next());
+        assertEquals(2.0, it.nextDouble());
+
+        PrimitiveDoubleArrayList recorder = new PrimitiveDoubleArrayList(16);
+        recorder.add(1.0);
+        recorder.add(2.0);
+        it.forEachRemaining((Double l) -> recorder.add(l));
+        assertEquals(Arrays.toString(data), Arrays.toString(recorder.toSortedArray()));
+
+        assertFalse(it.hasNext());
+    }
+
+    public void test_iterator_ref() {
+        String[] array = { "a", "b", "c", "d", "e", "f", "g", "h" };
+        Spliterator<String> stringSp = Spliterators.spliterator(array, 0);
+        Iterator<String> it = Spliterators.iterator(stringSp);
+
+        assertTrue(it.hasNext());
+        assertEquals("a", it.next());
+
+        ArrayList<String> recorder = new ArrayList<>();
+        recorder.add("a");
+        it.forEachRemaining((val) -> recorder.add(val));
+        assertEquals(Arrays.toString(array), Arrays.toString(recorder.toArray()));
+
+        assertFalse(it.hasNext());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/TreeMapTest.java b/luni/src/test/java/libcore/java/util/TreeMapTest.java
index 6864379..c60924d 100644
--- a/luni/src/test/java/libcore/java/util/TreeMapTest.java
+++ b/luni/src/test/java/libcore/java/util/TreeMapTest.java
@@ -17,6 +17,8 @@
 package libcore.java.util;
 
 import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Comparator;
 import java.util.ConcurrentModificationException;
 import java.util.HashMap;
@@ -24,8 +26,12 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.NavigableMap;
+import java.util.Set;
 import java.util.SortedMap;
+import java.util.Spliterator;
 import java.util.TreeMap;
+import java.util.WeakHashMap;
+
 import junit.framework.TestCase;
 import libcore.util.SerializationTester;
 
@@ -462,4 +468,103 @@
         treeMap.put("candy", "floss");
         treeMap.put("cheddar", "cheese");
     }
+
+    public void test_spliterator_keySet() {
+        TreeMap<String, String> treeMap = new TreeMap<>();
+        treeMap.put("a", "1");
+        treeMap.put("b", "2");
+        treeMap.put("c", "3");
+        treeMap.put("d", "4");
+        treeMap.put("e", "5");
+        treeMap.put("f", "6");
+        treeMap.put("g", "7");
+        treeMap.put("h", "8");
+        treeMap.put("i", "9");
+        treeMap.put("j", "10");
+        treeMap.put("k", "11");
+        treeMap.put("l", "12");
+        treeMap.put("m", "13");
+        treeMap.put("n", "14");
+        treeMap.put("o", "15");
+        treeMap.put("p", "16");
+
+        Set<String> keys = treeMap.keySet();
+        ArrayList<String> expectedKeys = new ArrayList<>(keys);
+
+        SpliteratorTester.runBasicIterationTests_unordered(keys.spliterator(), expectedKeys,
+                String::compareTo);
+        SpliteratorTester.runBasicSplitTests(keys, expectedKeys);
+        SpliteratorTester.testSpliteratorNPE(keys.spliterator());
+
+        assertTrue(keys.spliterator().hasCharacteristics(Spliterator.ORDERED | Spliterator.SORTED));
+        SpliteratorTester.runSortedTests(keys);
+        SpliteratorTester.runOrderedTests(keys);
+    }
+
+    public void test_spliterator_valueSet() {
+        TreeMap<String, String> treeMap = new TreeMap<>();
+        treeMap.put("a", "1");
+        treeMap.put("b", "2");
+        treeMap.put("c", "3");
+        treeMap.put("d", "4");
+        treeMap.put("e", "5");
+        treeMap.put("f", "6");
+        treeMap.put("g", "7");
+        treeMap.put("h", "8");
+        treeMap.put("i", "9");
+        treeMap.put("j", "10");
+        treeMap.put("k", "11");
+        treeMap.put("l", "12");
+        treeMap.put("m", "13");
+        treeMap.put("n", "14");
+        treeMap.put("o", "15");
+        treeMap.put("p", "16");
+
+        Collection<String> values = treeMap.values();
+        ArrayList<String> expectedValues = new ArrayList<>(values);
+
+        SpliteratorTester.runBasicIterationTests_unordered(
+                values.spliterator(), expectedValues, String::compareTo);
+        SpliteratorTester.runBasicSplitTests(values, expectedValues);
+        SpliteratorTester.testSpliteratorNPE(values.spliterator());
+
+        assertTrue(values.spliterator().hasCharacteristics(Spliterator.ORDERED | Spliterator.SORTED));
+        SpliteratorTester.runSortedTests(values);
+        SpliteratorTester.runOrderedTests(values);
+    }
+
+    public void test_spliterator_entrySet() {
+        TreeMap<String, String> treeMap = new TreeMap<>();
+        treeMap.put("a", "1");
+        treeMap.put("b", "2");
+        treeMap.put("c", "3");
+        treeMap.put("d", "4");
+        treeMap.put("e", "5");
+        treeMap.put("f", "6");
+        treeMap.put("g", "7");
+        treeMap.put("h", "8");
+        treeMap.put("i", "9");
+        treeMap.put("j", "10");
+        treeMap.put("k", "11");
+        treeMap.put("l", "12");
+        treeMap.put("m", "13");
+        treeMap.put("n", "14");
+        treeMap.put("o", "15");
+        treeMap.put("p", "16");
+
+        Set<Map.Entry<String, String>> values = treeMap.entrySet();
+        ArrayList<Map.Entry<String, String>> expectedValues = new ArrayList<>(values);
+
+        Comparator<Map.Entry<String, String>> comparator =
+                (a, b) -> (a.getKey().compareTo(b.getKey()));
+
+        SpliteratorTester.runBasicIterationTests_unordered(values.spliterator(), expectedValues,
+                (a, b) -> (a.getKey().compareTo(b.getKey())));
+        SpliteratorTester.runBasicSplitTests(values, expectedValues, comparator);
+        SpliteratorTester.testSpliteratorNPE(values.spliterator());
+
+        assertTrue(values.spliterator().hasCharacteristics(Spliterator.ORDERED | Spliterator.SORTED));
+        SpliteratorTester.runSortedTests(values, (a, b) -> (a.getKey().compareTo(b.getKey())));
+        SpliteratorTester.runOrderedTests(values);
+    }
 }
diff --git a/ojluni/src/main/java/java/lang/Iterable.java b/ojluni/src/main/java/java/lang/Iterable.java
old mode 100755
new mode 100644
index 05bd805..eed3938
--- a/ojluni/src/main/java/java/lang/Iterable.java
+++ b/ojluni/src/main/java/java/lang/Iterable.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,11 +22,12 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-
 package java.lang;
 
 import java.util.Iterator;
 import java.util.Objects;
+import java.util.Spliterator;
+import java.util.Spliterators;
 import java.util.function.Consumer;
 
 /**
@@ -42,9 +43,8 @@
  * @jls 14.14.2 The enhanced for statement
  */
 public interface Iterable<T> {
-
     /**
-     * Returns an iterator over a set of elements of type T.
+     * Returns an iterator over elements of type {@code T}.
      *
      * @return an Iterator.
      */
@@ -75,4 +75,29 @@
             action.accept(t);
         }
     }
+
+    /**
+     * Creates a {@link Spliterator} over the elements described by this
+     * {@code Iterable}.
+     *
+     * @implSpec
+     * The default implementation creates an
+     * <em><a href="Spliterator.html#binding">early-binding</a></em>
+     * spliterator from the iterable's {@code Iterator}.  The spliterator
+     * inherits the <em>fail-fast</em> properties of the iterable's iterator.
+     *
+     * @implNote
+     * The default implementation should usually be overridden.  The
+     * spliterator returned by the default implementation has poor splitting
+     * capabilities, is unsized, and does not report any spliterator
+     * characteristics. Implementing classes can nearly always provide a
+     * better implementation.
+     *
+     * @return a {@code Spliterator} over the elements described by this
+     * {@code Iterable}.
+     * @since 1.8
+     */
+    default Spliterator<T> spliterator() {
+        return Spliterators.spliteratorUnknownSize(iterator(), 0);
+    }
 }
diff --git a/ojluni/src/main/java/java/util/ArrayDeque.java b/ojluni/src/main/java/java/util/ArrayDeque.java
index 83b3925..eb31bcc 100644
--- a/ojluni/src/main/java/java/util/ArrayDeque.java
+++ b/ojluni/src/main/java/java/util/ArrayDeque.java
@@ -890,4 +890,99 @@
         for (int i = 0; i < size; i++)
             elements[i] = s.readObject();
     }
+
+    /**
+     * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
+     * and <em>fail-fast</em> {@link Spliterator} over the elements in this
+     * deque.
+     *
+     * <p>The {@code Spliterator} reports {@link Spliterator#SIZED},
+     * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
+     * {@link Spliterator#NONNULL}.  Overriding implementations should document
+     * the reporting of additional characteristic values.
+     *
+     * @return a {@code Spliterator} over the elements in this deque
+     * @since 1.8
+     */
+    public Spliterator<E> spliterator() {
+        return new DeqSpliterator<E>(this, -1, -1);
+    }
+
+    static final class DeqSpliterator<E> implements Spliterator<E> {
+        private final ArrayDeque<E> deq;
+        private int fence;  // -1 until first use
+        private int index;  // current index, modified on traverse/split
+
+        /** Creates new spliterator covering the given array and range */
+        DeqSpliterator(ArrayDeque<E> deq, int origin, int fence) {
+            this.deq = deq;
+            this.index = origin;
+            this.fence = fence;
+        }
+
+        private int getFence() { // force initialization
+            int t;
+            if ((t = fence) < 0) {
+                t = fence = deq.tail;
+                index = deq.head;
+            }
+            return t;
+        }
+
+        public DeqSpliterator<E> trySplit() {
+            int t = getFence(), h = index, n = deq.elements.length;
+            if (h != t && ((h + 1) & (n - 1)) != t) {
+                if (h > t)
+                    t += n;
+                int m = ((h + t) >>> 1) & (n - 1);
+                return new DeqSpliterator<>(deq, h, index = m);
+            }
+            return null;
+        }
+
+        public void forEachRemaining(Consumer<? super E> consumer) {
+            if (consumer == null)
+                throw new NullPointerException();
+            Object[] a = deq.elements;
+            int m = a.length - 1, f = getFence(), i = index;
+            index = f;
+            while (i != f) {
+                @SuppressWarnings("unchecked") E e = (E)a[i];
+                i = (i + 1) & m;
+                if (e == null)
+                    throw new ConcurrentModificationException();
+                consumer.accept(e);
+            }
+        }
+
+        public boolean tryAdvance(Consumer<? super E> consumer) {
+            if (consumer == null)
+                throw new NullPointerException();
+            Object[] a = deq.elements;
+            int m = a.length - 1, f = getFence(), i = index;
+            if (i != fence) {
+                @SuppressWarnings("unchecked") E e = (E)a[i];
+                index = (i + 1) & m;
+                if (e == null)
+                    throw new ConcurrentModificationException();
+                consumer.accept(e);
+                return true;
+            }
+            return false;
+        }
+
+        public long estimateSize() {
+            int n = getFence() - index;
+            if (n < 0)
+                n += deq.elements.length;
+            return (long) n;
+        }
+
+        @Override
+        public int characteristics() {
+            return Spliterator.ORDERED | Spliterator.SIZED |
+                Spliterator.NONNULL | Spliterator.SUBSIZED;
+        }
+    }
+
 }
diff --git a/ojluni/src/main/java/java/util/ArrayList.java b/ojluni/src/main/java/java/util/ArrayList.java
index f2cdfdd..c6ecd33 100755
--- a/ojluni/src/main/java/java/util/ArrayList.java
+++ b/ojluni/src/main/java/java/util/ArrayList.java
@@ -69,9 +69,9 @@
  * unsynchronized access to the list:<pre>
  *   List list = Collections.synchronizedList(new ArrayList(...));</pre>
  *
- * <p><a name="fail-fast"/>
+ * <p><a name="fail-fast">
  * The iterators returned by this class's {@link #iterator() iterator} and
- * {@link #listIterator(int) listIterator} methods are <em>fail-fast</em>:
+ * {@link #listIterator(int) listIterator} methods are <em>fail-fast</em>:</a>
  * if the list is structurally modified at any time after the iterator is
  * created, in any way except through the iterator's own
  * {@link ListIterator#remove() remove} or
@@ -334,14 +334,13 @@
      */
     public Object clone() {
         try {
-            @SuppressWarnings("unchecked")
-                ArrayList<E> v = (ArrayList<E>) super.clone();
+            ArrayList<?> v = (ArrayList<?>) super.clone();
             v.elementData = Arrays.copyOf(elementData, size);
             v.modCount = 0;
             return v;
         } catch (CloneNotSupportedException e) {
             // this shouldn't happen, since we are Cloneable
-            throw new InternalError();
+            throw new InternalError(e);
         }
     }
 
@@ -1231,4 +1230,140 @@
             throw new ConcurrentModificationException();
         }
     }
+
+    /**
+     * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
+     * and <em>fail-fast</em> {@link Spliterator} over the elements in this
+     * list.
+     *
+     * <p>The {@code Spliterator} reports {@link Spliterator#SIZED},
+     * {@link Spliterator#SUBSIZED}, and {@link Spliterator#ORDERED}.
+     * Overriding implementations should document the reporting of additional
+     * characteristic values.
+     *
+     * @return a {@code Spliterator} over the elements in this list
+     * @since 1.8
+     */
+    @Override
+    public Spliterator<E> spliterator() {
+        return new ArrayListSpliterator<>(this, 0, -1, 0);
+    }
+
+    /** Index-based split-by-two, lazily initialized Spliterator */
+    static final class ArrayListSpliterator<E> implements Spliterator<E> {
+
+        /*
+         * If ArrayLists were immutable, or structurally immutable (no
+         * adds, removes, etc), we could implement their spliterators
+         * with Arrays.spliterator. Instead we detect as much
+         * interference during traversal as practical without
+         * sacrificing much performance. We rely primarily on
+         * modCounts. These are not guaranteed to detect concurrency
+         * violations, and are sometimes overly conservative about
+         * within-thread interference, but detect enough problems to
+         * be worthwhile in practice. To carry this out, we (1) lazily
+         * initialize fence and expectedModCount until the latest
+         * point that we need to commit to the state we are checking
+         * against; thus improving precision.  (This doesn't apply to
+         * SubLists, that create spliterators with current non-lazy
+         * values).  (2) We perform only a single
+         * ConcurrentModificationException check at the end of forEach
+         * (the most performance-sensitive method). When using forEach
+         * (as opposed to iterators), we can normally only detect
+         * interference after actions, not before. Further
+         * CME-triggering checks apply to all other possible
+         * violations of assumptions for example null or too-small
+         * elementData array given its size(), that could only have
+         * occurred due to interference.  This allows the inner loop
+         * of forEach to run without any further checks, and
+         * simplifies lambda-resolution. While this does entail a
+         * number of checks, note that in the common case of
+         * list.stream().forEach(a), no checks or other computation
+         * occur anywhere other than inside forEach itself.  The other
+         * less-often-used methods cannot take advantage of most of
+         * these streamlinings.
+         */
+
+        private final ArrayList<E> list;
+        private int index; // current index, modified on advance/split
+        private int fence; // -1 until used; then one past last index
+        private int expectedModCount; // initialized when fence set
+
+        /** Create new spliterator covering the given  range */
+        ArrayListSpliterator(ArrayList<E> list, int origin, int fence,
+                             int expectedModCount) {
+            this.list = list; // OK if null unless traversed
+            this.index = origin;
+            this.fence = fence;
+            this.expectedModCount = expectedModCount;
+        }
+
+        private int getFence() { // initialize fence to size on first use
+            int hi; // (a specialized variant appears in method forEach)
+            ArrayList<E> lst;
+            if ((hi = fence) < 0) {
+                if ((lst = list) == null)
+                    hi = fence = 0;
+                else {
+                    expectedModCount = lst.modCount;
+                    hi = fence = lst.size;
+                }
+            }
+            return hi;
+        }
+
+        public ArrayListSpliterator<E> trySplit() {
+            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
+            return (lo >= mid) ? null : // divide range in half unless too small
+                new ArrayListSpliterator<E>(list, lo, index = mid,
+                                            expectedModCount);
+        }
+
+        public boolean tryAdvance(Consumer<? super E> action) {
+            if (action == null)
+                throw new NullPointerException();
+            int hi = getFence(), i = index;
+            if (i < hi) {
+                index = i + 1;
+                @SuppressWarnings("unchecked") E e = (E)list.elementData[i];
+                action.accept(e);
+                if (list.modCount != expectedModCount)
+                    throw new ConcurrentModificationException();
+                return true;
+            }
+            return false;
+        }
+
+        public void forEachRemaining(Consumer<? super E> action) {
+            int i, hi, mc; // hoist accesses and checks from loop
+            ArrayList<E> lst; Object[] a;
+            if (action == null)
+                throw new NullPointerException();
+            if ((lst = list) != null && (a = lst.elementData) != null) {
+                if ((hi = fence) < 0) {
+                    mc = lst.modCount;
+                    hi = lst.size;
+                }
+                else
+                    mc = expectedModCount;
+                if ((i = index) >= 0 && (index = hi) <= a.length) {
+                    for (; i < hi; ++i) {
+                        @SuppressWarnings("unchecked") E e = (E) a[i];
+                        action.accept(e);
+                    }
+                    if (lst.modCount == mc)
+                        return;
+                }
+            }
+            throw new ConcurrentModificationException();
+        }
+
+        public long estimateSize() {
+            return (long) (getFence() - index);
+        }
+
+        public int characteristics() {
+            return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
+        }
+    }
 }
diff --git a/ojluni/src/main/java/java/util/Arrays.java b/ojluni/src/main/java/java/util/Arrays.java
index c04d6cf..f39a719 100755
--- a/ojluni/src/main/java/java/util/Arrays.java
+++ b/ojluni/src/main/java/java/util/Arrays.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -2848,19 +2848,21 @@
         private final E[] a;
 
         ArrayList(E[] array) {
-            if (array==null)
-                throw new NullPointerException();
-            a = array;
+            a = Objects.requireNonNull(array);
         }
 
+        @Override
         public int size() {
             return a.length;
         }
 
+        @Override
         public Object[] toArray() {
             return a.clone();
         }
 
+        @Override
+        @SuppressWarnings("unchecked")
         public <T> T[] toArray(T[] a) {
             int size = size();
             if (a.length < size)
@@ -2872,16 +2874,19 @@
             return a;
         }
 
+        @Override
         public E get(int index) {
             return a[index];
         }
 
+        @Override
         public E set(int index, E element) {
             E oldValue = a[index];
             a[index] = element;
             return oldValue;
         }
 
+        @Override
         public int indexOf(Object o) {
             if (o==null) {
                 for (int i=0; i<a.length; i++)
@@ -2895,9 +2900,15 @@
             return -1;
         }
 
+        @Override
         public boolean contains(Object o) {
             return indexOf(o) != -1;
         }
+
+        @Override
+        public Spliterator<E> spliterator() {
+            return Spliterators.spliterator(a, Spliterator.ORDERED);
+        }
     }
 
     /**
@@ -3701,4 +3712,164 @@
                     count);
         }
     }
+
+
+    /**
+     * Returns a {@link Spliterator} covering all of the specified array.
+     *
+     * <p>The spliterator reports {@link Spliterator#SIZED},
+     * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
+     * {@link Spliterator#IMMUTABLE}.
+     *
+     * @param <T> type of elements
+     * @param array the array, assumed to be unmodified during use
+     * @return a spliterator for the array elements
+     * @since 1.8
+     */
+    public static <T> Spliterator<T> spliterator(T[] array) {
+        return Spliterators.spliterator(array,
+                                        Spliterator.ORDERED | Spliterator.IMMUTABLE);
+    }
+
+    /**
+     * Returns a {@link Spliterator} covering the specified range of the
+     * specified array.
+     *
+     * <p>The spliterator reports {@link Spliterator#SIZED},
+     * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
+     * {@link Spliterator#IMMUTABLE}.
+     *
+     * @param <T> type of elements
+     * @param array the array, assumed to be unmodified during use
+     * @param startInclusive the first index to cover, inclusive
+     * @param endExclusive index immediately past the last index to cover
+     * @return a spliterator for the array elements
+     * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is
+     *         negative, {@code endExclusive} is less than
+     *         {@code startInclusive}, or {@code endExclusive} is greater than
+     *         the array size
+     * @since 1.8
+     */
+    public static <T> Spliterator<T> spliterator(T[] array, int startInclusive, int endExclusive) {
+        return Spliterators.spliterator(array, startInclusive, endExclusive,
+                                        Spliterator.ORDERED | Spliterator.IMMUTABLE);
+    }
+
+    /**
+     * Returns a {@link Spliterator.OfInt} covering all of the specified array.
+     *
+     * <p>The spliterator reports {@link Spliterator#SIZED},
+     * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
+     * {@link Spliterator#IMMUTABLE}.
+     *
+     * @param array the array, assumed to be unmodified during use
+     * @return a spliterator for the array elements
+     * @since 1.8
+     */
+    public static Spliterator.OfInt spliterator(int[] array) {
+        return Spliterators.spliterator(array,
+                                        Spliterator.ORDERED | Spliterator.IMMUTABLE);
+    }
+
+    /**
+     * Returns a {@link Spliterator.OfInt} covering the specified range of the
+     * specified array.
+     *
+     * <p>The spliterator reports {@link Spliterator#SIZED},
+     * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
+     * {@link Spliterator#IMMUTABLE}.
+     *
+     * @param array the array, assumed to be unmodified during use
+     * @param startInclusive the first index to cover, inclusive
+     * @param endExclusive index immediately past the last index to cover
+     * @return a spliterator for the array elements
+     * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is
+     *         negative, {@code endExclusive} is less than
+     *         {@code startInclusive}, or {@code endExclusive} is greater than
+     *         the array size
+     * @since 1.8
+     */
+    public static Spliterator.OfInt spliterator(int[] array, int startInclusive, int endExclusive) {
+        return Spliterators.spliterator(array, startInclusive, endExclusive,
+                                        Spliterator.ORDERED | Spliterator.IMMUTABLE);
+    }
+
+    /**
+     * Returns a {@link Spliterator.OfLong} covering all of the specified array.
+     *
+     * <p>The spliterator reports {@link Spliterator#SIZED},
+     * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
+     * {@link Spliterator#IMMUTABLE}.
+     *
+     * @param array the array, assumed to be unmodified during use
+     * @return the spliterator for the array elements
+     * @since 1.8
+     */
+    public static Spliterator.OfLong spliterator(long[] array) {
+        return Spliterators.spliterator(array,
+                                        Spliterator.ORDERED | Spliterator.IMMUTABLE);
+    }
+
+    /**
+     * Returns a {@link Spliterator.OfLong} covering the specified range of the
+     * specified array.
+     *
+     * <p>The spliterator reports {@link Spliterator#SIZED},
+     * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
+     * {@link Spliterator#IMMUTABLE}.
+     *
+     * @param array the array, assumed to be unmodified during use
+     * @param startInclusive the first index to cover, inclusive
+     * @param endExclusive index immediately past the last index to cover
+     * @return a spliterator for the array elements
+     * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is
+     *         negative, {@code endExclusive} is less than
+     *         {@code startInclusive}, or {@code endExclusive} is greater than
+     *         the array size
+     * @since 1.8
+     */
+    public static Spliterator.OfLong spliterator(long[] array, int startInclusive, int endExclusive) {
+        return Spliterators.spliterator(array, startInclusive, endExclusive,
+                                        Spliterator.ORDERED | Spliterator.IMMUTABLE);
+    }
+
+    /**
+     * Returns a {@link Spliterator.OfDouble} covering all of the specified
+     * array.
+     *
+     * <p>The spliterator reports {@link Spliterator#SIZED},
+     * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
+     * {@link Spliterator#IMMUTABLE}.
+     *
+     * @param array the array, assumed to be unmodified during use
+     * @return a spliterator for the array elements
+     * @since 1.8
+     */
+    public static Spliterator.OfDouble spliterator(double[] array) {
+        return Spliterators.spliterator(array,
+                                        Spliterator.ORDERED | Spliterator.IMMUTABLE);
+    }
+
+    /**
+     * Returns a {@link Spliterator.OfDouble} covering the specified range of
+     * the specified array.
+     *
+     * <p>The spliterator reports {@link Spliterator#SIZED},
+     * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
+     * {@link Spliterator#IMMUTABLE}.
+     *
+     * @param array the array, assumed to be unmodified during use
+     * @param startInclusive the first index to cover, inclusive
+     * @param endExclusive index immediately past the last index to cover
+     * @return a spliterator for the array elements
+     * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is
+     *         negative, {@code endExclusive} is less than
+     *         {@code startInclusive}, or {@code endExclusive} is greater than
+     *         the array size
+     * @since 1.8
+     */
+    public static Spliterator.OfDouble spliterator(double[] array, int startInclusive, int endExclusive) {
+        return Spliterators.spliterator(array, startInclusive, endExclusive,
+                                        Spliterator.ORDERED | Spliterator.IMMUTABLE);
+    }
 }
diff --git a/ojluni/src/main/java/java/util/Collection.java b/ojluni/src/main/java/java/util/Collection.java
index b8f5869..72ee9ae 100755
--- a/ojluni/src/main/java/java/util/Collection.java
+++ b/ojluni/src/main/java/java/util/Collection.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -60,9 +60,9 @@
  * but is not required to, throw the exception if the collection to be added
  * is empty.
  *
- * <p><a name="optional-restrictions"/>
+ * <p><a name="optional-restrictions">
  * Some collection implementations have restrictions on the elements that
- * they may contain.  For example, some implementations prohibit null elements,
+ * they may contain.</a>  For example, some implementations prohibit null elements,
  * and some have restrictions on the types of their elements.  Attempting to
  * add an ineligible element throws an unchecked exception, typically
  * <tt>NullPointerException</tt> or <tt>ClassCastException</tt>.  Attempting
@@ -453,4 +453,60 @@
      * @see Object#equals(Object)
      */
     int hashCode();
+
+    // TODO: Restore links to #stream and #parallelStream.
+    /**
+     * Creates a {@link Spliterator} over the elements in this collection.
+     *
+     * Implementations should document characteristic values reported by the
+     * spliterator.  Such characteristic values are not required to be reported
+     * if the spliterator reports {@link Spliterator#SIZED} and this collection
+     * contains no elements.
+     *
+     * <p>The default implementation should be overridden by subclasses that
+     * can return a more efficient spliterator.  In order to
+     * preserve expected laziness behavior for the {@code stream} and
+     * {@code parallelStream()}} methods, spliterators should either have the
+     * characteristic of {@code IMMUTABLE} or {@code CONCURRENT}, or be
+     * <em><a href="Spliterator.html#binding">late-binding</a></em>.
+     * If none of these is practical, the overriding class should describe the
+     * spliterator's documented policy of binding and structural interference,
+     * and should override the {@code stream} and {@code parallelStream}
+     * methods to create streams using a {@code Supplier} of the spliterator,
+     * as in:
+     * <pre>{@code
+     *     Stream<E> s = StreamSupport.stream(() -> spliterator(), spliteratorCharacteristics)
+     * }</pre>
+     * <p>These requirements ensure that streams produced by the
+     * {@code stream} and {@code parallelStream} methods will reflect the
+     * contents of the collection as of initiation of the terminal stream
+     * operation.
+     *
+     * @implSpec
+     * The default implementation creates a
+     * <em><a href="Spliterator.html#binding">late-binding</a></em> spliterator
+     * from the collections's {@code Iterator}.  The spliterator inherits the
+     * <em>fail-fast</em> properties of the collection's iterator.
+     * <p>
+     * The created {@code Spliterator} reports {@link Spliterator#SIZED}.
+     *
+     * @implNote
+     * The created {@code Spliterator} additionally reports
+     * {@link Spliterator#SUBSIZED}.
+     *
+     * <p>If a spliterator covers no elements then the reporting of additional
+     * characteristic values, beyond that of {@code SIZED} and {@code SUBSIZED},
+     * does not aid clients to control, specialize or simplify computation.
+     * However, this does enable shared use of an immutable and empty
+     * spliterator instance (see {@link Spliterators#emptySpliterator()}) for
+     * empty collections, and enables clients to determine if such a spliterator
+     * covers no elements.
+     *
+     * @return a {@code Spliterator} over the elements in this collection
+     * @since 1.8
+     */
+    @Override
+    default Spliterator<E> spliterator() {
+        return Spliterators.spliterator(this, 0);
+    }
 }
diff --git a/ojluni/src/main/java/java/util/Collections.java b/ojluni/src/main/java/java/util/Collections.java
index 47a1ecc..8baf0e7 100755
--- a/ojluni/src/main/java/java/util/Collections.java
+++ b/ojluni/src/main/java/java/util/Collections.java
@@ -31,6 +31,7 @@
 import java.io.Serializable;
 import java.lang.reflect.Array;
 import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
 import java.util.function.Consumer;
 
 
@@ -135,7 +136,7 @@
      *
      * <p>The implementation was adapted from Tim Peters's list sort for Python
      * (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
-     * TimSort</a>).  It uses techiques from Peter McIlroy's "Optimistic
+     * TimSort</a>).  It uses techniques from Peter McIlroy's "Optimistic
      * Sorting and Information Theoretic Complexity", in Proceedings of the
      * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
      * January 1993.
@@ -146,6 +147,7 @@
      * n<sup>2</sup> log(n) performance that would result from attempting
      * to sort a linked list in place.
      *
+     * @param  <T> the class of the objects in the list
      * @param  list the list to be sorted.
      * @throws ClassCastException if the list contains elements that are not
      *         <i>mutually comparable</i> (for example, strings and integers).
@@ -155,6 +157,7 @@
      *         detects that the natural ordering of the list elements is
      *         found to violate the {@link Comparable} contract
      */
+    @SuppressWarnings("unchecked")
     public static <T extends Comparable<? super T>> void sort(List<T> list) {
         if (list.getClass() == ArrayList.class) {
             Arrays.sort(((ArrayList) list).elementData, 0, list.size());
@@ -200,7 +203,7 @@
      *
      * <p>The implementation was adapted from Tim Peters's list sort for Python
      * (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
-     * TimSort</a>).  It uses techiques from Peter McIlroy's "Optimistic
+     * TimSort</a>).  It uses techniques from Peter McIlroy's "Optimistic
      * Sorting and Information Theoretic Complexity", in Proceedings of the
      * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
      * January 1993.
@@ -211,6 +214,7 @@
      * n<sup>2</sup> log(n) performance that would result from attempting
      * to sort a linked list in place.
      *
+     * @param  <T> the class of the objects in the list
      * @param  list the list to be sorted.
      * @param  c the comparator to determine the order of the list.  A
      *        {@code null} value indicates that the elements' <i>natural
@@ -222,6 +226,7 @@
      * @throws IllegalArgumentException (optional) if the comparator is
      *         found to violate the {@link Comparator} contract
      */
+    @SuppressWarnings({"unchecked", "rawtypes"})
     public static <T> void sort(List<T> list, Comparator<? super T> c) {
         if (list.getClass() == ArrayList.class) {
             Arrays.sort(((ArrayList) list).elementData, 0, list.size(), (Comparator) c);
@@ -230,10 +235,10 @@
 
         Object[] a = list.toArray();
         Arrays.sort(a, (Comparator)c);
-        ListIterator i = list.listIterator();
+        ListIterator<T> i = list.listIterator();
         for (int j=0; j<a.length; j++) {
             i.next();
-            i.set(a[j]);
+            i.set((T)a[j]);
         }
     }
 
@@ -253,6 +258,7 @@
      * this method will do an iterator-based binary search that performs
      * O(n) link traversals and O(log n) element comparisons.
      *
+     * @param  <T> the class of the objects in the list
      * @param  list the list to be searched.
      * @param  key the key to be searched for.
      * @return the index of the search key, if it is contained in the list;
@@ -277,8 +283,7 @@
     }
 
     private static <T>
-    int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key)
-    {
+    int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) {
         int low = 0;
         int high = list.size()-1;
 
@@ -354,6 +359,7 @@
      * this method will do an iterator-based binary search that performs
      * O(n) link traversals and O(log n) element comparisons.
      *
+     * @param  <T> the class of the objects in the list
      * @param  list the list to be searched.
      * @param  key the key to be searched for.
      * @param  c the comparator by which the list is ordered.
@@ -372,9 +378,10 @@
      *         or the search key is not mutually comparable with the
      *         elements of the list using this comparator.
      */
+    @SuppressWarnings("unchecked")
     public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c) {
         if (c==null)
-            return binarySearch((List) list, key);
+            return binarySearch((List<? extends Comparable<? super T>>) list, key);
 
         if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
             return Collections.indexedBinarySearch(list, key, c);
@@ -421,9 +428,6 @@
         return -(low + 1);  // key not found
     }
 
-    private interface SelfComparable extends Comparable<SelfComparable> {}
-
-
     /**
      * Reverses the order of the elements in the specified list.<p>
      *
@@ -433,12 +437,16 @@
      * @throws UnsupportedOperationException if the specified list or
      *         its list-iterator does not support the <tt>set</tt> operation.
      */
+    @SuppressWarnings({"rawtypes", "unchecked"})
     public static void reverse(List<?> list) {
         int size = list.size();
         if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) {
             for (int i=0, mid=size>>1, j=size-1; i<mid; i++, j--)
                 swap(list, i, j);
         } else {
+            // instead of using a raw type here, it's possible to capture
+            // the wildcard but it will require a call to a supplementary
+            // private method
             ListIterator fwd = list.listIterator();
             ListIterator rev = list.listIterator(size);
             for (int i=0, mid=list.size()>>1; i<mid; i++) {
@@ -452,21 +460,21 @@
     /**
      * Randomly permutes the specified list using a default source of
      * randomness.  All permutations occur with approximately equal
-     * likelihood.<p>
+     * likelihood.
      *
-     * The hedge "approximately" is used in the foregoing description because
+     * <p>The hedge "approximately" is used in the foregoing description because
      * default source of randomness is only approximately an unbiased source
      * of independently chosen bits. If it were a perfect source of randomly
      * chosen bits, then the algorithm would choose permutations with perfect
-     * uniformity.<p>
+     * uniformity.
      *
-     * This implementation traverses the list backwards, from the last element
-     * up to the second, repeatedly swapping a randomly selected element into
-     * the "current position".  Elements are randomly selected from the
+     * <p>This implementation traverses the list backwards, from the last
+     * element up to the second, repeatedly swapping a randomly selected element
+     * into the "current position".  Elements are randomly selected from the
      * portion of the list that runs from the first element to the current
-     * position, inclusive.<p>
+     * position, inclusive.
      *
-     * This method runs in linear time.  If the specified list does not
+     * <p>This method runs in linear time.  If the specified list does not
      * implement the {@link RandomAccess} interface and is large, this
      * implementation dumps the specified list into an array before shuffling
      * it, and dumps the shuffled array back into the list.  This avoids the
@@ -480,9 +488,10 @@
     public static void shuffle(List<?> list) {
         Random rnd = r;
         if (rnd == null)
-            r = rnd = new Random();
+            r = rnd = new Random(); // harmless race.
         shuffle(list, rnd);
     }
+
     private static Random r;
 
     /**
@@ -508,6 +517,7 @@
      * @throws UnsupportedOperationException if the specified list or its
      *         list-iterator does not support the <tt>set</tt> operation.
      */
+    @SuppressWarnings({"rawtypes", "unchecked"})
     public static void shuffle(List<?> list, Random rnd) {
         int size = list.size();
         if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
@@ -521,6 +531,9 @@
                 swap(arr, i-1, rnd.nextInt(i));
 
             // Dump array back into list
+            // instead of using a raw type here, it's possible to capture
+            // the wildcard but it will require a call to a supplementary
+            // private method
             ListIterator it = list.listIterator();
             for (int i=0; i<arr.length; i++) {
                 it.next();
@@ -542,7 +555,11 @@
      *         || j &lt; 0 || j &gt;= list.size()).
      * @since 1.4
      */
+    @SuppressWarnings({"rawtypes", "unchecked"})
     public static void swap(List<?> list, int i, int j) {
+        // instead of using a raw type here, it's possible to capture
+        // the wildcard but it will require a call to a supplementary
+        // private method
         final List l = list;
         l.set(i, l.set(j, l.get(i)));
     }
@@ -562,6 +579,7 @@
      *
      * This method runs in linear time.
      *
+     * @param  <T> the class of the objects in the list
      * @param  list the list to be filled with the specified element.
      * @param  obj The element with which to fill the specified list.
      * @throws UnsupportedOperationException if the specified list or its
@@ -591,6 +609,7 @@
      *
      * This method runs in linear time.
      *
+     * @param  <T> the class of the objects in the lists
      * @param  dest The destination list.
      * @param  src The source list.
      * @throws IndexOutOfBoundsException if the destination list is too small
@@ -629,6 +648,7 @@
      * This method iterates over the entire collection, hence it requires
      * time proportional to the size of the collection.
      *
+     * @param  <T> the class of the objects in the collection
      * @param  coll the collection whose minimum element is to be determined.
      * @return the minimum element of the given collection, according
      *         to the <i>natural ordering</i> of its elements.
@@ -661,6 +681,7 @@
      * This method iterates over the entire collection, hence it requires
      * time proportional to the size of the collection.
      *
+     * @param  <T> the class of the objects in the collection
      * @param  coll the collection whose minimum element is to be determined.
      * @param  comp the comparator with which to determine the minimum element.
      *         A <tt>null</tt> value indicates that the elements' <i>natural
@@ -672,9 +693,10 @@
      * @throws NoSuchElementException if the collection is empty.
      * @see Comparable
      */
+    @SuppressWarnings({"unchecked", "rawtypes"})
     public static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp) {
         if (comp==null)
-            return (T)min((Collection<SelfComparable>) (Collection) coll);
+            return (T)min((Collection) coll);
 
         Iterator<? extends T> i = coll.iterator();
         T candidate = i.next();
@@ -699,6 +721,7 @@
      * This method iterates over the entire collection, hence it requires
      * time proportional to the size of the collection.
      *
+     * @param  <T> the class of the objects in the collection
      * @param  coll the collection whose maximum element is to be determined.
      * @return the maximum element of the given collection, according
      *         to the <i>natural ordering</i> of its elements.
@@ -731,6 +754,7 @@
      * This method iterates over the entire collection, hence it requires
      * time proportional to the size of the collection.
      *
+     * @param  <T> the class of the objects in the collection
      * @param  coll the collection whose maximum element is to be determined.
      * @param  comp the comparator with which to determine the maximum element.
      *         A <tt>null</tt> value indicates that the elements' <i>natural
@@ -742,9 +766,10 @@
      * @throws NoSuchElementException if the collection is empty.
      * @see Comparable
      */
+    @SuppressWarnings({"unchecked", "rawtypes"})
     public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) {
         if (comp==null)
-            return (T)max((Collection<SelfComparable>) (Collection) coll);
+            return (T)max((Collection) coll);
 
         Iterator<? extends T> i = coll.iterator();
         T candidate = i.next();
@@ -864,6 +889,7 @@
      * <tt>(oldVal==null ? e==null : oldVal.equals(e))</tt>.
      * (This method has no effect on the size of the list.)
      *
+     * @param  <T> the class of the objects in the list
      * @param list the list in which replacement is to occur.
      * @param oldVal the old value to be replaced.
      * @param newVal the new value with which <tt>oldVal</tt> is to be
@@ -919,9 +945,9 @@
      * Returns the starting position of the first occurrence of the specified
      * target list within the specified source list, or -1 if there is no
      * such occurrence.  More formally, returns the lowest index <tt>i</tt>
-     * such that <tt>source.subList(i, i+target.size()).equals(target)</tt>,
+     * such that {@code source.subList(i, i+target.size()).equals(target)},
      * or -1 if there is no such index.  (Returns -1 if
-     * <tt>target.size() > source.size()</tt>.)
+     * {@code target.size() > source.size()})
      *
      * <p>This implementation uses the "brute force" technique of scanning
      * over the source list, looking for a match with the target at each
@@ -972,9 +998,9 @@
      * Returns the starting position of the last occurrence of the specified
      * target list within the specified source list, or -1 if there is no such
      * occurrence.  More formally, returns the highest index <tt>i</tt>
-     * such that <tt>source.subList(i, i+target.size()).equals(target)</tt>,
+     * such that {@code source.subList(i, i+target.size()).equals(target)},
      * or -1 if there is no such index.  (Returns -1 if
-     * <tt>target.size() > source.size()</tt>.)
+     * {@code target.size() > source.size()})
      *
      * <p>This implementation uses the "brute force" technique of iterating
      * over the source list, looking for a match with the target at each
@@ -1045,6 +1071,7 @@
      * The returned collection will be serializable if the specified collection
      * is serializable.
      *
+     * @param  <T> the class of the objects in the collection
      * @param  c the collection for which an unmodifiable view is to be
      *         returned.
      * @return an unmodifiable view of the specified collection.
@@ -1119,6 +1146,11 @@
         public void forEach(Consumer<? super E> action) {
             c.forEach(action);
         }
+        @SuppressWarnings("unchecked")
+        @Override
+        public Spliterator<E> spliterator() {
+            return (Spliterator<E>)c.spliterator();
+        }
     }
 
     /**
@@ -1131,6 +1163,7 @@
      * The returned set will be serializable if the specified set
      * is serializable.
      *
+     * @param  <T> the class of the objects in the set
      * @param  s the set for which an unmodifiable view is to be returned.
      * @return an unmodifiable view of the specified set.
      */
@@ -1162,6 +1195,7 @@
      * The returned sorted set will be serializable if the specified sorted set
      * is serializable.
      *
+     * @param  <T> the class of the objects in the set
      * @param s the sorted set for which an unmodifiable view is to be
      *        returned.
      * @return an unmodifiable view of the specified sorted set.
@@ -1209,6 +1243,7 @@
      * is serializable. Similarly, the returned list will implement
      * {@link RandomAccess} if the specified list does.
      *
+     * @param  <T> the class of the objects in the list
      * @param  list the list for which an unmodifiable view is to be returned.
      * @return an unmodifiable view of the specified list.
      */
@@ -1224,6 +1259,7 @@
     static class UnmodifiableList<E> extends UnmodifiableCollection<E>
                                   implements List<E> {
         private static final long serialVersionUID = -283967356065247728L;
+
         final List<? extends E> list;
 
         UnmodifiableList(List<? extends E> list) {
@@ -1342,6 +1378,8 @@
      * The returned map will be serializable if the specified map
      * is serializable.
      *
+     * @param <K> the class of the map keys
+     * @param <V> the class of the map values
      * @param  m the map for which an unmodifiable view is to be returned.
      * @return an unmodifiable view of the specified map.
      */
@@ -1426,7 +1464,9 @@
             extends UnmodifiableSet<Map.Entry<K,V>> {
             private static final long serialVersionUID = 7854390611657943733L;
 
+            @SuppressWarnings({"unchecked", "rawtypes"})
             UnmodifiableEntrySet(Set<? extends Map.Entry<? extends K, ? extends V>> s) {
+                // Need to cast to raw in order to work around a limitation in the type system
                 super((Set)s);
             }
 
@@ -1440,6 +1480,66 @@
                 c.forEach(entryConsumer(action));
             }
 
+            static final class UnmodifiableEntrySetSpliterator<K, V>
+                    implements Spliterator<Entry<K,V>> {
+                final Spliterator<Map.Entry<K, V>> s;
+
+                UnmodifiableEntrySetSpliterator(Spliterator<Entry<K, V>> s) {
+                    this.s = s;
+                }
+
+                @Override
+                public boolean tryAdvance(Consumer<? super Entry<K, V>> action) {
+                    Objects.requireNonNull(action);
+                    return s.tryAdvance(entryConsumer(action));
+                }
+
+                @Override
+                public void forEachRemaining(Consumer<? super Entry<K, V>> action) {
+                    Objects.requireNonNull(action);
+                    s.forEachRemaining(entryConsumer(action));
+                }
+
+                @Override
+                public Spliterator<Entry<K, V>> trySplit() {
+                    Spliterator<Entry<K, V>> split = s.trySplit();
+                    return split == null
+                           ? null
+                           : new UnmodifiableEntrySetSpliterator<>(split);
+                }
+
+                @Override
+                public long estimateSize() {
+                    return s.estimateSize();
+                }
+
+                @Override
+                public long getExactSizeIfKnown() {
+                    return s.getExactSizeIfKnown();
+                }
+
+                @Override
+                public int characteristics() {
+                    return s.characteristics();
+                }
+
+                @Override
+                public boolean hasCharacteristics(int characteristics) {
+                    return s.hasCharacteristics(characteristics);
+                }
+
+                @Override
+                public Comparator<? super Entry<K, V>> getComparator() {
+                    return s.getComparator();
+                }
+            }
+
+            @SuppressWarnings("unchecked")
+            public Spliterator<Entry<K,V>> spliterator() {
+                return new UnmodifiableEntrySetSpliterator<>(
+                        (Spliterator<Map.Entry<K, V>>) c.spliterator());
+            }
+
             public Iterator<Map.Entry<K,V>> iterator() {
                 return new Iterator<Map.Entry<K,V>>() {
                     private final Iterator<? extends Map.Entry<? extends K, ? extends V>> i = c.iterator();
@@ -1458,13 +1558,15 @@
                 };
             }
 
+            @SuppressWarnings("unchecked")
             public Object[] toArray() {
                 Object[] a = c.toArray();
                 for (int i=0; i<a.length; i++)
-                    a[i] = new UnmodifiableEntry<>((Map.Entry<K,V>)a[i]);
+                    a[i] = new UnmodifiableEntry<>((Map.Entry<? extends K, ? extends V>)a[i]);
                 return a;
             }
 
+            @SuppressWarnings("unchecked")
             public <T> T[] toArray(T[] a) {
                 // We don't pass a to c.toArray, to avoid window of
                 // vulnerability wherein an unscrupulous multithreaded client
@@ -1472,7 +1574,7 @@
                 Object[] arr = c.toArray(a.length==0 ? a : Arrays.copyOf(a, 0));
 
                 for (int i=0; i<arr.length; i++)
-                    arr[i] = new UnmodifiableEntry<>((Map.Entry<K,V>)arr[i]);
+                    arr[i] = new UnmodifiableEntry<>((Map.Entry<? extends K, ? extends V>)arr[i]);
 
                 if (arr.length > a.length)
                     return (T[])arr;
@@ -1514,7 +1616,7 @@
 
                 if (!(o instanceof Set))
                     return false;
-                Set s = (Set) o;
+                Set<?> s = (Set<?>) o;
                 if (s.size() != c.size())
                     return false;
                 return containsAll(s); // Invokes safe containsAll() above
@@ -1530,7 +1632,8 @@
             private static class UnmodifiableEntry<K,V> implements Map.Entry<K,V> {
                 private Map.Entry<? extends K, ? extends V> e;
 
-                UnmodifiableEntry(Map.Entry<? extends K, ? extends V> e) {this.e = e;}
+                UnmodifiableEntry(Map.Entry<? extends K, ? extends V> e)
+                        {this.e = Objects.requireNonNull(e);}
 
                 public K getKey()        {return e.getKey();}
                 public V getValue()      {return e.getValue();}
@@ -1543,7 +1646,7 @@
                         return true;
                     if (!(o instanceof Map.Entry))
                         return false;
-                    Map.Entry t = (Map.Entry)o;
+                    Map.Entry<?,?> t = (Map.Entry<?,?>)o;
                     return eq(e.getKey(),   t.getKey()) &&
                            eq(e.getValue(), t.getValue());
                 }
@@ -1564,6 +1667,8 @@
      * The returned sorted map will be serializable if the specified sorted map
      * is serializable.
      *
+     * @param <K> the class of the map keys
+     * @param <V> the class of the map values
      * @param m the sorted map for which an unmodifiable view is to be
      *        returned.
      * @return an unmodifiable view of the specified sorted map.
@@ -1582,27 +1687,21 @@
 
         private final SortedMap<K, ? extends V> sm;
 
-        UnmodifiableSortedMap(SortedMap<K, ? extends V> m) {super(m); sm = m;}
-
-        public Comparator<? super K> comparator() {return sm.comparator();}
-
-        public SortedMap<K,V> subMap(K fromKey, K toKey) {
-            return new UnmodifiableSortedMap<>(sm.subMap(fromKey, toKey));
-        }
-        public SortedMap<K,V> headMap(K toKey) {
-            return new UnmodifiableSortedMap<>(sm.headMap(toKey));
-        }
-        public SortedMap<K,V> tailMap(K fromKey) {
-            return new UnmodifiableSortedMap<>(sm.tailMap(fromKey));
-        }
-
-        public K firstKey()           {return sm.firstKey();}
-        public K lastKey()            {return sm.lastKey();}
+        UnmodifiableSortedMap(SortedMap<K, ? extends V> m) {super(m); sm = m; }
+        public Comparator<? super K> comparator()   { return sm.comparator(); }
+        public SortedMap<K,V> subMap(K fromKey, K toKey)
+             { return new UnmodifiableSortedMap<>(sm.subMap(fromKey, toKey)); }
+        public SortedMap<K,V> headMap(K toKey)
+                     { return new UnmodifiableSortedMap<>(sm.headMap(toKey)); }
+        public SortedMap<K,V> tailMap(K fromKey)
+                   { return new UnmodifiableSortedMap<>(sm.tailMap(fromKey)); }
+        public K firstKey()                           { return sm.firstKey(); }
+        public K lastKey()                             { return sm.lastKey(); }
     }
 
-
     // Synch Wrappers
 
+    // TODO: Replace {@code stream} with {@link Stream} once we have them.
     /**
      * Returns a synchronized (thread-safe) collection backed by the specified
      * collection.  In order to guarantee serial access, it is critical that
@@ -1610,7 +1709,8 @@
      * through the returned collection.<p>
      *
      * It is imperative that the user manually synchronize on the returned
-     * collection when iterating over it:
+     * collection when traversing it via {@link Iterator}, {@link Spliterator}
+     * or {@code Stream}:
      * <pre>
      *  Collection c = Collections.synchronizedCollection(myCollection);
      *     ...
@@ -1622,15 +1722,16 @@
      * </pre>
      * Failure to follow this advice may result in non-deterministic behavior.
      *
-     * <p>The returned collection does <i>not</i> pass the <tt>hashCode</tt>
-     * and <tt>equals</tt> operations through to the backing collection, but
-     * relies on <tt>Object</tt>'s equals and hashCode methods.  This is
+     * <p>The returned collection does <i>not</i> pass the {@code hashCode}
+     * and {@code equals} operations through to the backing collection, but
+     * relies on {@code Object}'s equals and hashCode methods.  This is
      * necessary to preserve the contracts of these operations in the case
      * that the backing collection is a set or a list.<p>
      *
      * The returned collection will be serializable if the specified collection
      * is serializable.
      *
+     * @param  <T> the class of the objects in the collection
      * @param  c the collection to be "wrapped" in a synchronized collection.
      * @return a synchronized view of the specified collection.
      */
@@ -1652,14 +1753,13 @@
         final Object mutex;     // Object on which to synchronize
 
         SynchronizedCollection(Collection<E> c) {
-            if (c==null)
-                throw new NullPointerException();
-            this.c = c;
+            this.c = Objects.requireNonNull(c);
             mutex = this;
         }
+
         SynchronizedCollection(Collection<E> c, Object mutex) {
-            this.c = c;
-            this.mutex = mutex;
+            this.c = Objects.requireNonNull(c);
+            this.mutex = Objects.requireNonNull(mutex);
         }
 
         public int size() {
@@ -1707,12 +1807,15 @@
         public String toString() {
             synchronized (mutex) {return c.toString();}
         }
-
         // Override default methods in Collection
         @Override
         public void forEach(Consumer<? super E> consumer) {
             synchronized (mutex) {c.forEach(consumer);}
         }
+        @Override
+        public Spliterator<E> spliterator() {
+            return c.spliterator(); // Must be manually synched by user!
+        }
 
         private void writeObject(ObjectOutputStream s) throws IOException {
             synchronized (mutex) {s.defaultWriteObject();}
@@ -1741,6 +1844,7 @@
      * <p>The returned set will be serializable if the specified set is
      * serializable.
      *
+     * @param  <T> the class of the objects in the set
      * @param  s the set to be "wrapped" in a synchronized set.
      * @return a synchronized view of the specified set.
      */
@@ -1811,6 +1915,7 @@
      * <p>The returned sorted set will be serializable if the specified
      * sorted set is serializable.
      *
+     * @param  <T> the class of the objects in the set
      * @param  s the sorted set to be "wrapped" in a synchronized sorted set.
      * @return a synchronized view of the specified sorted set.
      */
@@ -1889,6 +1994,7 @@
      * <p>The returned list will be serializable if the specified list is
      * serializable.
      *
+     * @param  <T> the class of the objects in the list
      * @param  list the list to be "wrapped" in a synchronized list.
      * @return a synchronized view of the specified list.
      */
@@ -2049,6 +2155,8 @@
      * <p>The returned map will be serializable if the specified map is
      * serializable.
      *
+     * @param <K> the class of the map keys
+     * @param <V> the class of the map values
      * @param  m the map to be "wrapped" in a synchronized map.
      * @return a synchronized view of the specified map.
      */
@@ -2067,9 +2175,7 @@
         final Object      mutex;        // Object on which to synchronize
 
         SynchronizedMap(Map<K,V> m) {
-            if (m==null)
-                throw new NullPointerException();
-            this.m = m;
+            this.m = Objects.requireNonNull(m);
             mutex = this;
         }
 
@@ -2196,6 +2302,8 @@
      * <p>The returned sorted map will be serializable if the specified
      * sorted map is serializable.
      *
+     * @param <K> the class of the map keys
+     * @param <V> the class of the map values
      * @param  m the sorted map to be "wrapped" in a synchronized sorted map.
      * @return a synchronized view of the specified sorted map.
      */
@@ -2203,7 +2311,6 @@
         return new SynchronizedSortedMap<>(m);
     }
 
-
     /**
      * @serial include
      */
@@ -2284,12 +2391,12 @@
      * program to wrap the collection with a dynamically typesafe view.
      * For example, this declaration:
      *  <pre> {@code
-     *     Collection<String> c = new HashSet<String>();
+     *     Collection<String> c = new HashSet<>();
      * }</pre>
      * may be replaced temporarily by this one:
      *  <pre> {@code
      *     Collection<String> c = Collections.checkedCollection(
-     *         new HashSet<String>(), String.class);
+     *         new HashSet<>(), String.class);
      * }</pre>
      * Running the program again will cause it to fail at the point where
      * an incorrectly typed element is inserted into the collection, clearly
@@ -2309,6 +2416,7 @@
      * type, the returned collection permits insertion of null elements
      * whenever the backing collection does.
      *
+     * @param <E> the class of the objects in the collection
      * @param c the collection for which a dynamically typesafe view is to be
      *          returned
      * @param type the type of element that {@code c} is permitted to hold
@@ -2371,6 +2479,8 @@
         }
 
         public Iterator<E> iterator() {
+            // JDK-6363904 - unwrapped iterator could be typecast to
+            // ListIterator with unsafe set()
             final Iterator<E> it = c.iterator();
             return new Iterator<E>() {
                 public boolean hasNext() { return it.hasNext(); }
@@ -2425,6 +2535,8 @@
         // Override default methods in Collection
         @Override
         public void forEach(Consumer<? super E> action) {c.forEach(action);}
+        @Override
+        public Spliterator<E> spliterator() {return c.spliterator();}
     }
 
     /**
@@ -2447,6 +2559,7 @@
      * type, the returned set permits insertion of null elements whenever
      * the backing set does.
      *
+     * @param <E> the class of the objects in the set
      * @param s the set for which a dynamically typesafe view is to be
      *          returned
      * @param type the type of element that {@code s} is permitted to hold
@@ -2492,6 +2605,7 @@
      * type, the returned sorted set permits insertion of null elements
      * whenever the backing sorted set does.
      *
+     * @param <E> the class of the objects in the set
      * @param s the sorted set for which a dynamically typesafe view is to be
      *          returned
      * @param type the type of element that {@code s} is permitted to hold
@@ -2510,6 +2624,7 @@
         implements SortedSet<E>, Serializable
     {
         private static final long serialVersionUID = 1599911165492914959L;
+
         private final SortedSet<E> ss;
 
         CheckedSortedSet(SortedSet<E> s, Class<E> type) {
@@ -2552,6 +2667,7 @@
      * type, the returned list permits insertion of null elements whenever
      * the backing list does.
      *
+     * @param <E> the class of the objects in the list
      * @param list the list for which a dynamically typesafe view is to be
      *             returned
      * @param type the type of element that {@code list} is permitted to hold
@@ -2649,7 +2765,7 @@
 
         public List<E> subList(int fromIndex, int toIndex) {
             return new CheckedRandomAccessList<>(
-                list.subList(fromIndex, toIndex), type);
+                    list.subList(fromIndex, toIndex), type);
         }
     }
 
@@ -2680,6 +2796,8 @@
      * type, the returned map permits insertion of null keys or values
      * whenever the backing map does.
      *
+     * @param <K> the class of the map keys
+     * @param <V> the class of the map values
      * @param m the map for which a dynamically typesafe view is to be
      *          returned
      * @param keyType the type of key that {@code m} is permitted to hold
@@ -2714,6 +2832,16 @@
                 throw new ClassCastException(badValueMsg(value));
         }
 
+        private BiFunction<? super K, ? super V, ? extends V> typeCheck(
+                BiFunction<? super K, ? super V, ? extends V> func) {
+            Objects.requireNonNull(func);
+            return (k, v) -> {
+                V newValue = func.apply(k, v);
+                typeCheck(k, newValue);
+                return newValue;
+            };
+        }
+
         private String badKeyMsg(Object key) {
             return "Attempt to insert " + key.getClass() +
                 " key into map with key type " + keyType;
@@ -2725,11 +2853,9 @@
         }
 
         CheckedMap(Map<K, V> m, Class<K> keyType, Class<V> valueType) {
-            if (m == null || keyType == null || valueType == null)
-                throw new NullPointerException();
-            this.m = m;
-            this.keyType = keyType;
-            this.valueType = valueType;
+            this.m = Objects.requireNonNull(m);
+            this.keyType = Objects.requireNonNull(keyType);
+            this.valueType = Objects.requireNonNull(valueType);
         }
 
         public int size()                      { return m.size(); }
@@ -2765,7 +2891,7 @@
                 Object v = e.getValue();
                 typeCheck(k, v);
                 checked.add(
-                    new AbstractMap.SimpleImmutableEntry<>((K) k, (V) v));
+                        new AbstractMap.SimpleImmutableEntry<>((K)k, (V)v));
             }
             for (Map.Entry<K,V> e : checked)
                 m.put(e.getKey(), e.getValue());
@@ -2907,6 +3033,7 @@
                 return batchRemove(c, true);
             }
             private boolean batchRemove(Collection<?> c, boolean complement) {
+                Objects.requireNonNull(c);
                 boolean modified = false;
                 Iterator<Map.Entry<K,V>> it = iterator();
                 while (it.hasNext()) {
@@ -2945,8 +3072,8 @@
                 private final Class<T> valueType;
 
                 CheckedEntry(Map.Entry<K, V> e, Class<T> valueType) {
-                    this.e = e;
-                    this.valueType = valueType;
+                    this.e = Objects.requireNonNull(e);
+                    this.valueType = Objects.requireNonNull(valueType);
                 }
 
                 public K getKey()        { return e.getKey(); }
@@ -3004,6 +3131,8 @@
      * type, the returned map permits insertion of null keys or values
      * whenever the backing map does.
      *
+     * @param <K> the class of the map keys
+     * @param <V> the class of the map values
      * @param m the map for which a dynamically typesafe view is to be
      *          returned
      * @param keyType the type of key that {@code m} is permitted to hold
@@ -3054,22 +3183,19 @@
     /**
      * Returns an iterator that has no elements.  More precisely,
      *
-     * <ul compact>
-     *
+     * <ul>
      * <li>{@link Iterator#hasNext hasNext} always returns {@code
-     * false}.
-     *
+     * false}.</li>
      * <li>{@link Iterator#next next} always throws {@link
-     * NoSuchElementException}.
-     *
+     * NoSuchElementException}.</li>
      * <li>{@link Iterator#remove remove} always throws {@link
-     * IllegalStateException}.
-     *
+     * IllegalStateException}.</li>
      * </ul>
      *
      * <p>Implementations of this method are permitted, but not
      * required, to return the same object from multiple invocations.
      *
+     * @param <T> type of elements, if there were any, in the iterator
      * @return an empty iterator
      * @since 1.7
      */
@@ -3094,32 +3220,26 @@
     /**
      * Returns a list iterator that has no elements.  More precisely,
      *
-     * <ul compact>
-     *
+     * <ul>
      * <li>{@link Iterator#hasNext hasNext} and {@link
      * ListIterator#hasPrevious hasPrevious} always return {@code
-     * false}.
-     *
+     * false}.</li>
      * <li>{@link Iterator#next next} and {@link ListIterator#previous
-     * previous} always throw {@link NoSuchElementException}.
-     *
+     * previous} always throw {@link NoSuchElementException}.</li>
      * <li>{@link Iterator#remove remove} and {@link ListIterator#set
-     * set} always throw {@link IllegalStateException}.
-     *
+     * set} always throw {@link IllegalStateException}.</li>
      * <li>{@link ListIterator#add add} always throws {@link
-     * UnsupportedOperationException}.
-     *
+     * UnsupportedOperationException}.</li>
      * <li>{@link ListIterator#nextIndex nextIndex} always returns
-     * {@code 0} .
-     *
+     * {@code 0}.</li>
      * <li>{@link ListIterator#previousIndex previousIndex} always
-     * returns {@code -1}.
-     *
+     * returns {@code -1}.</li>
      * </ul>
      *
      * <p>Implementations of this method are permitted, but not
      * required, to return the same object from multiple invocations.
      *
+     * @param <T> type of elements, if there were any, in the iterator
      * @return an empty list iterator
      * @since 1.7
      */
@@ -3146,19 +3266,17 @@
     /**
      * Returns an enumeration that has no elements.  More precisely,
      *
-     * <ul compact>
-     *
+     * <ul>
      * <li>{@link Enumeration#hasMoreElements hasMoreElements} always
-     * returns {@code false}.
-     *
+     * returns {@code false}.</li>
      * <li> {@link Enumeration#nextElement nextElement} always throws
-     * {@link NoSuchElementException}.
-     *
+     * {@link NoSuchElementException}.</li>
      * </ul>
      *
      * <p>Implementations of this method are permitted, but not
      * required, to return the same object from multiple invocations.
      *
+     * @param  <T> the class of the objects in the enumeration
      * @return an empty enumeration
      * @since 1.7
      */
@@ -3180,21 +3298,24 @@
      *
      * @see #emptySet()
      */
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings("rawtypes")
     public static final Set EMPTY_SET = new EmptySet<>();
 
     /**
-     * Returns the empty set (immutable).  This set is serializable.
+     * Returns an empty set (immutable).  This set is serializable.
      * Unlike the like-named field, this method is parameterized.
      *
      * <p>This example illustrates the type-safe way to obtain an empty set:
      * <pre>
      *     Set&lt;String&gt; s = Collections.emptySet();
      * </pre>
-     * Implementation note:  Implementations of this method need not
-     * create a separate <tt>Set</tt> object for each call.   Using this
-     * method is likely to have comparable cost to using the like-named
-     * field.  (Unlike this method, the field does not provide type safety.)
+     * @implNote Implementations of this method need not create a separate
+     * {@code Set} object for each call.  Using this method is likely to have
+     * comparable cost to using the like-named field.  (Unlike this method, the
+     * field does not provide type safety.)
+     *
+     * @param  <T> the class of the objects in the set
+     * @return the empty set
      *
      * @see #EMPTY_SET
      * @since 1.5
@@ -3229,16 +3350,19 @@
             return a;
         }
 
-        // Preserves singleton property
-        private Object readResolve() {
-            return EMPTY_SET;
-        }
-
         // Override default methods in Collection
         @Override
         public void forEach(Consumer<? super E> action) {
             Objects.requireNonNull(action);
         }
+        @Override
+        public Spliterator<E> spliterator() { return Spliterators.emptySpliterator(); }
+
+        // Preserves singleton property
+        private Object readResolve() {
+            return EMPTY_SET;
+        }
+
     }
 
     /**
@@ -3246,11 +3370,11 @@
      *
      * @see #emptyList()
      */
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings("rawtypes")
     public static final List EMPTY_LIST = new EmptyList<>();
 
     /**
-     * Returns the empty list (immutable).  This list is serializable.
+     * Returns an empty list (immutable).  This list is serializable.
      *
      * <p>This example illustrates the type-safe way to obtain an empty list:
      * <pre>
@@ -3261,6 +3385,9 @@
      * method is likely to have comparable cost to using the like-named
      * field.  (Unlike this method, the field does not provide type safety.)
      *
+     * @param <T> type of elements, if there were any, in the list
+     * @return an empty immutable list
+     *
      * @see #EMPTY_LIST
      * @since 1.5
      */
@@ -3308,16 +3435,19 @@
 
         public int hashCode() { return 1; }
 
-        // Preserves singleton property
-        private Object readResolve() {
-            return EMPTY_LIST;
-        }
-
         // Override default methods in Collection
         @Override
         public void forEach(Consumer<? super E> action) {
             Objects.requireNonNull(action);
         }
+
+        @Override
+        public Spliterator<E> spliterator() { return Spliterators.emptySpliterator(); }
+
+        // Preserves singleton property
+        private Object readResolve() {
+            return EMPTY_LIST;
+        }
     }
 
     /**
@@ -3326,21 +3456,24 @@
      * @see #emptyMap()
      * @since 1.3
      */
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings("rawtypes")
     public static final Map EMPTY_MAP = new EmptyMap<>();
 
     /**
-     * Returns the empty map (immutable).  This map is serializable.
+     * Returns an empty map (immutable).  This map is serializable.
      *
-     * <p>This example illustrates the type-safe way to obtain an empty set:
+     * <p>This example illustrates the type-safe way to obtain an empty map:
      * <pre>
      *     Map&lt;String, Date&gt; s = Collections.emptyMap();
      * </pre>
-     * Implementation note:  Implementations of this method need not
-     * create a separate <tt>Map</tt> object for each call.   Using this
-     * method is likely to have comparable cost to using the like-named
-     * field.  (Unlike this method, the field does not provide type safety.)
+     * @implNote Implementations of this method need not create a separate
+     * {@code Map} object for each call.  Using this method is likely to have
+     * comparable cost to using the like-named field.  (Unlike this method, the
+     * field does not provide type safety.)
      *
+     * @param <K> the class of the map keys
+     * @param <V> the class of the map values
+     * @return an empty map
      * @see #EMPTY_MAP
      * @since 1.5
      */
@@ -3373,16 +3506,16 @@
 
         public int hashCode()                      {return 0;}
 
-        // Preserves singleton property
-        private Object readResolve() {
-            return EMPTY_MAP;
-        }
-
         // Override default methods in Map
         @Override
         public void forEach(BiConsumer<? super K, ? super V> action) {
             Objects.requireNonNull(action);
         }
+
+        // Preserves singleton property
+        private Object readResolve() {
+            return EMPTY_MAP;
+        }
     }
 
     // Singleton collections
diff --git a/ojluni/src/main/java/java/util/ConcurrentModificationException.java b/ojluni/src/main/java/java/util/ConcurrentModificationException.java
index 3683fb3..d525653 100755
--- a/ojluni/src/main/java/java/util/ConcurrentModificationException.java
+++ b/ojluni/src/main/java/java/util/ConcurrentModificationException.java
@@ -57,6 +57,7 @@
  * @author  Josh Bloch
  * @see     Collection
  * @see     Iterator
+ * @see     Spliterator
  * @see     ListIterator
  * @see     Vector
  * @see     LinkedList
diff --git a/ojluni/src/main/java/java/util/HashMap.java b/ojluni/src/main/java/java/util/HashMap.java
index e4cad69..ca771fe 100755
--- a/ojluni/src/main/java/java/util/HashMap.java
+++ b/ojluni/src/main/java/java/util/HashMap.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -980,6 +980,263 @@
         }
     }
 
+        /* ------------------------------------------------------------ */
+    // spliterators
+
+    static class HashMapSpliterator<K,V> {
+        final HashMap<K,V> map;
+        HashMapEntry<K,V> current;  // current entry
+        int index;                  // current index, modified on advance/split
+        int fence;                  // one past last index
+        int est;                    // size estimate
+        int expectedModCount;       // for comodification checks
+
+        HashMapSpliterator(HashMap<K,V> m, int origin,
+                           int fence, int est,
+                           int expectedModCount) {
+            this.map = m;
+            this.index = origin;
+            this.fence = fence;
+            this.est = est;
+            this.expectedModCount = expectedModCount;
+        }
+
+        final int getFence() { // initialize fence and size on first use
+            int hi;
+            if ((hi = fence) < 0) {
+                HashMap<K,V> m = map;
+                est = m.size;
+                expectedModCount = m.modCount;
+                HashMapEntry<K,V>[] tab = m.table;
+                hi = fence = (tab == null) ? 0 : tab.length;
+            }
+            return hi;
+        }
+
+        public final long estimateSize() {
+            getFence(); // force init
+            return (long) est;
+        }
+    }
+
+    static final class KeySpliterator<K,V>
+            extends HashMapSpliterator<K,V>
+            implements Spliterator<K> {
+        KeySpliterator(HashMap<K,V> m, int origin, int fence, int est,
+                       int expectedModCount) {
+            super(m, origin, fence, est, expectedModCount);
+        }
+
+        public KeySpliterator<K,V> trySplit() {
+            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
+            return (lo >= mid || current != null) ? null :
+                    new KeySpliterator<>(map, lo, index = mid, est >>>= 1,
+                            expectedModCount);
+        }
+
+        public void forEachRemaining(Consumer<? super K> action) {
+            int i, hi, mc;
+            if (action == null)
+                throw new NullPointerException();
+            HashMap<K,V> m = map;
+            HashMapEntry<K,V>[] tab = m.table;
+            if ((hi = fence) < 0) {
+                mc = expectedModCount = m.modCount;
+                hi = fence = (tab == null) ? 0 : tab.length;
+            }
+            else
+                mc = expectedModCount;
+            if (tab != null && tab.length >= hi &&
+                    (i = index) >= 0 && (i < (index = hi) || current != null)) {
+                HashMapEntry<K,V> p = current;
+                current = null;
+                do {
+                    if (p == null)
+                        p = tab[i++];
+                    else {
+                        action.accept(p.key);
+                        p = p.next;
+                    }
+                } while (p != null || i < hi);
+                if (m.modCount != mc)
+                    throw new ConcurrentModificationException();
+            }
+        }
+
+        public boolean tryAdvance(Consumer<? super K> action) {
+            int hi;
+            if (action == null)
+                throw new NullPointerException();
+            HashMapEntry<K,V>[] tab = map.table;
+            if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
+                while (current != null || index < hi) {
+                    if (current == null)
+                        current = tab[index++];
+                    else {
+                        K k = current.key;
+                        current = current.next;
+                        action.accept(k);
+                        if (map.modCount != expectedModCount)
+                            throw new ConcurrentModificationException();
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        public int characteristics() {
+            return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
+                    Spliterator.DISTINCT |
+                    ((map instanceof LinkedHashMap) ? Spliterator.ORDERED : 0);
+        }
+    }
+
+    static final class ValueSpliterator<K,V>
+            extends HashMapSpliterator<K,V>
+            implements Spliterator<V> {
+        ValueSpliterator(HashMap<K,V> m, int origin, int fence, int est,
+                         int expectedModCount) {
+            super(m, origin, fence, est, expectedModCount);
+        }
+
+        public ValueSpliterator<K,V> trySplit() {
+            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
+            return (lo >= mid || current != null) ? null :
+                    new ValueSpliterator<>(map, lo, index = mid, est >>>= 1,
+                            expectedModCount);
+        }
+
+        public void forEachRemaining(Consumer<? super V> action) {
+            int i, hi, mc;
+            if (action == null)
+                throw new NullPointerException();
+            HashMap<K,V> m = map;
+            HashMapEntry<K,V>[] tab = m.table;
+            if ((hi = fence) < 0) {
+                mc = expectedModCount = m.modCount;
+                hi = fence = (tab == null) ? 0 : tab.length;
+            }
+            else
+                mc = expectedModCount;
+            if (tab != null && tab.length >= hi &&
+                    (i = index) >= 0 && (i < (index = hi) || current != null)) {
+                HashMapEntry<K,V> p = current;
+                current = null;
+                do {
+                    if (p == null)
+                        p = tab[i++];
+                    else {
+                        action.accept(p.value);
+                        p = p.next;
+                    }
+                } while (p != null || i < hi);
+                if (m.modCount != mc)
+                    throw new ConcurrentModificationException();
+            }
+        }
+
+        public boolean tryAdvance(Consumer<? super V> action) {
+            int hi;
+            if (action == null)
+                throw new NullPointerException();
+            HashMapEntry<K,V>[] tab = map.table;
+            if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
+                while (current != null || index < hi) {
+                    if (current == null)
+                        current = tab[index++];
+                    else {
+                        V v = current.value;
+                        current = current.next;
+                        action.accept(v);
+                        if (map.modCount != expectedModCount)
+                            throw new ConcurrentModificationException();
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        public int characteristics() {
+            return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
+                    ((map instanceof LinkedHashMap) ? Spliterator.ORDERED : 0);
+        }
+    }
+
+    static final class EntrySpliterator<K,V>
+            extends HashMapSpliterator<K,V>
+            implements Spliterator<Map.Entry<K,V>> {
+        EntrySpliterator(HashMap<K,V> m, int origin, int fence, int est,
+                         int expectedModCount) {
+            super(m, origin, fence, est, expectedModCount);
+        }
+
+        public EntrySpliterator<K,V> trySplit() {
+            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
+            return (lo >= mid || current != null) ? null :
+                    new EntrySpliterator<>(map, lo, index = mid, est >>>= 1,
+                            expectedModCount);
+        }
+
+        public void forEachRemaining(Consumer<? super Map.Entry<K,V>> action) {
+            int i, hi, mc;
+            if (action == null)
+                throw new NullPointerException();
+            HashMap<K,V> m = map;
+            HashMapEntry<K,V>[] tab = m.table;
+            if ((hi = fence) < 0) {
+                mc = expectedModCount = m.modCount;
+                hi = fence = (tab == null) ? 0 : tab.length;
+            }
+            else
+                mc = expectedModCount;
+            if (tab != null && tab.length >= hi &&
+                    (i = index) >= 0 && (i < (index = hi) || current != null)) {
+                HashMapEntry<K,V> p = current;
+                current = null;
+                do {
+                    if (p == null)
+                        p = tab[i++];
+                    else {
+                        action.accept(p);
+                        p = p.next;
+                    }
+                } while (p != null || i < hi);
+                if (m.modCount != mc)
+                    throw new ConcurrentModificationException();
+            }
+        }
+
+        public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {
+            int hi;
+            if (action == null)
+                throw new NullPointerException();
+            HashMapEntry<K,V>[] tab = map.table;
+            if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
+                while (current != null || index < hi) {
+                    if (current == null)
+                        current = tab[index++];
+                    else {
+                        HashMapEntry<K,V> e = current;
+                        current = current.next;
+                        action.accept(e);
+                        if (map.modCount != expectedModCount)
+                            throw new ConcurrentModificationException();
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        public int characteristics() {
+            return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
+                    Spliterator.DISTINCT |
+                    ((map instanceof LinkedHashMap) ? Spliterator.ORDERED : 0);
+        }
+    }
+
     // Subclass overrides these to alter behavior of views' iterator() method
     Iterator<K> newKeyIterator()   {
         return new KeyIterator();
@@ -1030,6 +1287,9 @@
         public void clear() {
             HashMap.this.clear();
         }
+        public final Spliterator<K> spliterator() {
+            return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
+        }
         public final void forEach(Consumer<? super K> action) {
             HashMapEntry<K,V>[] tab;
             if (action == null)
@@ -1082,6 +1342,9 @@
         public void clear() {
             HashMap.this.clear();
         }
+        public final Spliterator<V> spliterator() {
+            return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
+        }
         public final void forEach(Consumer<? super V> action) {
             HashMapEntry<K,V>[] tab;
             if (action == null)
@@ -1149,6 +1412,9 @@
         public void clear() {
             HashMap.this.clear();
         }
+        public final Spliterator<Map.Entry<K,V>> spliterator() {
+            return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
+        }
         public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
             HashMapEntry<K,V>[] tab;
             if (action == null)
diff --git a/ojluni/src/main/java/java/util/HashSet.java b/ojluni/src/main/java/java/util/HashSet.java
index 9e53917..c8deade 100755
--- a/ojluni/src/main/java/java/util/HashSet.java
+++ b/ojluni/src/main/java/java/util/HashSet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -309,4 +309,20 @@
             map.put(e, PRESENT);
         }
     }
+
+    /**
+     * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
+     * and <em>fail-fast</em> {@link Spliterator} over the elements in this
+     * set.
+     *
+     * <p>The {@code Spliterator} reports {@link Spliterator#SIZED} and
+     * {@link Spliterator#DISTINCT}.  Overriding implementations should document
+     * the reporting of additional characteristic values.
+     *
+     * @return a {@code Spliterator} over the elements in this set
+     * @since 1.8
+     */
+    public Spliterator<E> spliterator() {
+        return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0);
+    }
 }
diff --git a/ojluni/src/main/java/java/util/IdentityHashMap.java b/ojluni/src/main/java/java/util/IdentityHashMap.java
index b0847ec..ea1948d 100755
--- a/ojluni/src/main/java/java/util/IdentityHashMap.java
+++ b/ojluni/src/main/java/java/util/IdentityHashMap.java
@@ -26,6 +26,7 @@
 package java.util;
 
 import java.io.*;
+import java.lang.reflect.Array;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
@@ -330,6 +331,7 @@
      *
      * @see #put(Object, Object)
      */
+    @SuppressWarnings("unchecked")
     public V get(Object key) {
         Object k = maskNull(key);
         Object[] tab = table;
@@ -434,7 +436,8 @@
         Object item;
         while ( (item = tab[i]) != null) {
             if (item == k) {
-                V oldValue = (V) tab[i + 1];
+                @SuppressWarnings("unchecked")
+                    V oldValue = (V) tab[i + 1];
                 tab[i + 1] = value;
                 return oldValue;
             }
@@ -527,7 +530,8 @@
             if (item == k) {
                 modCount++;
                 size--;
-                V oldValue = (V) tab[i + 1];
+                @SuppressWarnings("unchecked")
+                    V oldValue = (V) tab[i + 1];
                 tab[i + 1] = null;
                 tab[i] = null;
                 closeDeletion(i);
@@ -641,7 +645,7 @@
         if (o == this) {
             return true;
         } else if (o instanceof IdentityHashMap) {
-            IdentityHashMap m = (IdentityHashMap) o;
+            IdentityHashMap<?,?> m = (IdentityHashMap<?,?>) o;
             if (m.size() != size)
                 return false;
 
@@ -653,7 +657,7 @@
             }
             return true;
         } else if (o instanceof Map) {
-            Map m = (Map)o;
+            Map<?,?> m = (Map<?,?>)o;
             return entrySet().equals(m.entrySet());
         } else {
             return false;  // o is not a Map
@@ -701,12 +705,12 @@
      */
     public Object clone() {
         try {
-            IdentityHashMap<K,V> m = (IdentityHashMap<K,V>) super.clone();
+            IdentityHashMap<?,?> m = (IdentityHashMap<?,?>) super.clone();
             m.entrySet = null;
             m.table = table.clone();
             return m;
         } catch (CloneNotSupportedException e) {
-            throw new InternalError();
+            throw new InternalError(e);
         }
     }
 
@@ -771,7 +775,7 @@
             int len = tab.length;
 
             int d = deletedSlot;
-            K key = (K) tab[d];
+            Object key = tab[d];
             tab[d] = null;        // vacate the slot
             tab[d + 1] = null;
 
@@ -821,12 +825,14 @@
     }
 
     private class KeyIterator extends IdentityHashMapIterator<K> {
+        @SuppressWarnings("unchecked")
         public K next() {
             return (K) unmaskNull(traversalTable[nextIndex()]);
         }
     }
 
     private class ValueIterator extends IdentityHashMapIterator<V> {
+        @SuppressWarnings("unchecked")
         public V next() {
             return (V) traversalTable[nextIndex() + 1];
         }
@@ -857,16 +863,19 @@
                 this.index = index;
             }
 
+            @SuppressWarnings("unchecked")
             public K getKey() {
                 checkIndexForEntryUse();
                 return (K) unmaskNull(traversalTable[index]);
             }
 
+            @SuppressWarnings("unchecked")
             public V getValue() {
                 checkIndexForEntryUse();
                 return (V) traversalTable[index+1];
             }
 
+            @SuppressWarnings("unchecked")
             public V setValue(V value) {
                 checkIndexForEntryUse();
                 V oldValue = (V) traversalTable[index+1];
@@ -883,7 +892,7 @@
 
                 if (!(o instanceof Map.Entry))
                     return false;
-                Map.Entry e = (Map.Entry)o;
+                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                 return (e.getKey() == unmaskNull(traversalTable[index]) &&
                        e.getValue() == traversalTable[index+1]);
             }
@@ -987,6 +996,7 @@
          * behavior when c is a smaller "normal" (non-identity-based) Set.
          */
         public boolean removeAll(Collection<?> c) {
+            Objects.requireNonNull(c);
             boolean modified = false;
             for (Iterator<K> i = iterator(); i.hasNext(); ) {
                 if (c.contains(i.next())) {
@@ -1005,6 +1015,41 @@
                 result += System.identityHashCode(key);
             return result;
         }
+        public Object[] toArray() {
+            return toArray(new Object[0]);
+        }
+        @SuppressWarnings("unchecked")
+        public <T> T[] toArray(T[] a) {
+            int expectedModCount = modCount;
+            int size = size();
+            if (a.length < size)
+                a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);
+            Object[] tab = table;
+            int ti = 0;
+            for (int si = 0; si < tab.length; si += 2) {
+                Object key;
+                if ((key = tab[si]) != null) { // key present ?
+                    // more elements than expected -> concurrent modification from other thread
+                    if (ti >= size) {
+                        throw new ConcurrentModificationException();
+                    }
+                    a[ti++] = (T) unmaskNull(key); // unmask key
+                }
+            }
+            // fewer elements than expected or concurrent modification from other thread detected
+            if (ti < size || expectedModCount != modCount) {
+                throw new ConcurrentModificationException();
+            }
+            // final null marker as per spec
+            if (ti < a.length) {
+                a[ti] = null;
+            }
+            return a;
+        }
+
+        public Spliterator<K> spliterator() {
+            return new KeySpliterator<>(IdentityHashMap.this, 0, -1, 0, 0);
+        }
     }
 
     /**
@@ -1057,6 +1102,40 @@
         public void clear() {
             IdentityHashMap.this.clear();
         }
+        public Object[] toArray() {
+            return toArray(new Object[0]);
+        }
+        @SuppressWarnings("unchecked")
+        public <T> T[] toArray(T[] a) {
+            int expectedModCount = modCount;
+            int size = size();
+            if (a.length < size)
+                a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);
+            Object[] tab = table;
+            int ti = 0;
+            for (int si = 0; si < tab.length; si += 2) {
+                if (tab[si] != null) { // key present ?
+                    // more elements than expected -> concurrent modification from other thread
+                    if (ti >= size) {
+                        throw new ConcurrentModificationException();
+                    }
+                    a[ti++] = (T) tab[si+1]; // copy value
+                }
+            }
+            // fewer elements than expected or concurrent modification from other thread detected
+            if (ti < size || expectedModCount != modCount) {
+                throw new ConcurrentModificationException();
+            }
+            // final null marker as per spec
+            if (ti < a.length) {
+                a[ti] = null;
+            }
+            return a;
+        }
+
+        public Spliterator<V> spliterator() {
+            return new ValueSpliterator<>(IdentityHashMap.this, 0, -1, 0, 0);
+        }
     }
 
     /**
@@ -1112,13 +1191,13 @@
         public boolean contains(Object o) {
             if (!(o instanceof Map.Entry))
                 return false;
-            Map.Entry entry = (Map.Entry)o;
+            Map.Entry<?,?> entry = (Map.Entry<?,?>)o;
             return containsMapping(entry.getKey(), entry.getValue());
         }
         public boolean remove(Object o) {
             if (!(o instanceof Map.Entry))
                 return false;
-            Map.Entry entry = (Map.Entry)o;
+            Map.Entry<?,?> entry = (Map.Entry<?,?>)o;
             return removeMapping(entry.getKey(), entry.getValue());
         }
         public int size() {
@@ -1133,6 +1212,7 @@
          * behavior when c is a smaller "normal" (non-identity-based) Set.
          */
         public boolean removeAll(Collection<?> c) {
+            Objects.requireNonNull(c);
             boolean modified = false;
             for (Iterator<Map.Entry<K,V>> i = iterator(); i.hasNext(); ) {
                 if (c.contains(i.next())) {
@@ -1144,27 +1224,41 @@
         }
 
         public Object[] toArray() {
-            int size = size();
-            Object[] result = new Object[size];
-            Iterator<Map.Entry<K,V>> it = iterator();
-            for (int i = 0; i < size; i++)
-                result[i] = new AbstractMap.SimpleEntry<>(it.next());
-            return result;
+            return toArray(new Object[0]);
         }
 
         @SuppressWarnings("unchecked")
         public <T> T[] toArray(T[] a) {
+            int expectedModCount = modCount;
             int size = size();
             if (a.length < size)
-                a = (T[])java.lang.reflect.Array
-                    .newInstance(a.getClass().getComponentType(), size);
-            Iterator<Map.Entry<K,V>> it = iterator();
-            for (int i = 0; i < size; i++)
-                a[i] = (T) new AbstractMap.SimpleEntry<>(it.next());
-            if (a.length > size)
-                a[size] = null;
+                a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);
+            Object[] tab = table;
+            int ti = 0;
+            for (int si = 0; si < tab.length; si += 2) {
+                Object key;
+                if ((key = tab[si]) != null) { // key present ?
+                    // more elements than expected -> concurrent modification from other thread
+                    if (ti >= size) {
+                        throw new ConcurrentModificationException();
+                    }
+                    a[ti++] = (T) new AbstractMap.SimpleEntry<>(unmaskNull(key), tab[si + 1]);
+                }
+            }
+            // fewer elements than expected or concurrent modification from other thread detected
+            if (ti < size || expectedModCount != modCount) {
+                throw new ConcurrentModificationException();
+            }
+            // final null marker as per spec
+            if (ti < a.length) {
+                a[ti] = null;
+            }
             return a;
         }
+
+        public Spliterator<Map.Entry<K,V>> spliterator() {
+            return new EntrySpliterator<>(IdentityHashMap.this, 0, -1, 0, 0);
+        }
     }
 
 
@@ -1216,8 +1310,10 @@
 
         // Read the keys and values, and put the mappings in the table
         for (int i=0; i<size; i++) {
-            K key = (K) s.readObject();
-            V value = (V) s.readObject();
+            @SuppressWarnings("unchecked")
+                K key = (K) s.readObject();
+            @SuppressWarnings("unchecked")
+                V value = (V) s.readObject();
             putForCreate(key, value);
         }
     }
@@ -1229,7 +1325,7 @@
     private void putForCreate(K key, V value)
         throws IOException
     {
-        K k = (K)maskNull(key);
+        Object k = maskNull(key);
         Object[] tab = table;
         int len = tab.length;
         int i = hash(k, len);
@@ -1263,4 +1359,222 @@
         }
     }
 
+    /**
+     * Similar form as array-based Spliterators, but skips blank elements,
+     * and guestimates size as decreasing by half per split.
+     */
+    static class IdentityHashMapSpliterator<K,V> {
+        final IdentityHashMap<K,V> map;
+        int index;             // current index, modified on advance/split
+        int fence;             // -1 until first use; then one past last index
+        int est;               // size estimate
+        int expectedModCount;  // initialized when fence set
+
+        IdentityHashMapSpliterator(IdentityHashMap<K,V> map, int origin,
+                                   int fence, int est, int expectedModCount) {
+            this.map = map;
+            this.index = origin;
+            this.fence = fence;
+            this.est = est;
+            this.expectedModCount = expectedModCount;
+        }
+
+        final int getFence() { // initialize fence and size on first use
+            int hi;
+            if ((hi = fence) < 0) {
+                est = map.size;
+                expectedModCount = map.modCount;
+                hi = fence = map.table.length;
+            }
+            return hi;
+        }
+
+        public final long estimateSize() {
+            getFence(); // force init
+            return (long) est;
+        }
+    }
+
+    static final class KeySpliterator<K,V>
+        extends IdentityHashMapSpliterator<K,V>
+        implements Spliterator<K> {
+        KeySpliterator(IdentityHashMap<K,V> map, int origin, int fence, int est,
+                       int expectedModCount) {
+            super(map, origin, fence, est, expectedModCount);
+        }
+
+        public KeySpliterator<K,V> trySplit() {
+            int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1;
+            return (lo >= mid) ? null :
+                new KeySpliterator<K,V>(map, lo, index = mid, est >>>= 1,
+                                        expectedModCount);
+        }
+
+        @SuppressWarnings("unchecked")
+        public void forEachRemaining(Consumer<? super K> action) {
+            if (action == null)
+                throw new NullPointerException();
+            int i, hi, mc; Object key;
+            IdentityHashMap<K,V> m; Object[] a;
+            if ((m = map) != null && (a = m.table) != null &&
+                (i = index) >= 0 && (index = hi = getFence()) <= a.length) {
+                for (; i < hi; i += 2) {
+                    if ((key = a[i]) != null)
+                        action.accept((K)unmaskNull(key));
+                }
+                if (m.modCount == expectedModCount)
+                    return;
+            }
+            throw new ConcurrentModificationException();
+        }
+
+        @SuppressWarnings("unchecked")
+        public boolean tryAdvance(Consumer<? super K> action) {
+            if (action == null)
+                throw new NullPointerException();
+            Object[] a = map.table;
+            int hi = getFence();
+            while (index < hi) {
+                Object key = a[index];
+                index += 2;
+                if (key != null) {
+                    action.accept((K)unmaskNull(key));
+                    if (map.modCount != expectedModCount)
+                        throw new ConcurrentModificationException();
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public int characteristics() {
+            return (fence < 0 || est == map.size ? SIZED : 0) | Spliterator.DISTINCT;
+        }
+    }
+
+    static final class ValueSpliterator<K,V>
+        extends IdentityHashMapSpliterator<K,V>
+        implements Spliterator<V> {
+        ValueSpliterator(IdentityHashMap<K,V> m, int origin, int fence, int est,
+                         int expectedModCount) {
+            super(m, origin, fence, est, expectedModCount);
+        }
+
+        public ValueSpliterator<K,V> trySplit() {
+            int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1;
+            return (lo >= mid) ? null :
+                new ValueSpliterator<K,V>(map, lo, index = mid, est >>>= 1,
+                                          expectedModCount);
+        }
+
+        public void forEachRemaining(Consumer<? super V> action) {
+            if (action == null)
+                throw new NullPointerException();
+            int i, hi, mc;
+            IdentityHashMap<K,V> m; Object[] a;
+            if ((m = map) != null && (a = m.table) != null &&
+                (i = index) >= 0 && (index = hi = getFence()) <= a.length) {
+                for (; i < hi; i += 2) {
+                    if (a[i] != null) {
+                        @SuppressWarnings("unchecked") V v = (V)a[i+1];
+                        action.accept(v);
+                    }
+                }
+                if (m.modCount == expectedModCount)
+                    return;
+            }
+            throw new ConcurrentModificationException();
+        }
+
+        public boolean tryAdvance(Consumer<? super V> action) {
+            if (action == null)
+                throw new NullPointerException();
+            Object[] a = map.table;
+            int hi = getFence();
+            while (index < hi) {
+                Object key = a[index];
+                @SuppressWarnings("unchecked") V v = (V)a[index+1];
+                index += 2;
+                if (key != null) {
+                    action.accept(v);
+                    if (map.modCount != expectedModCount)
+                        throw new ConcurrentModificationException();
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public int characteristics() {
+            return (fence < 0 || est == map.size ? SIZED : 0);
+        }
+
+    }
+
+    static final class EntrySpliterator<K,V>
+        extends IdentityHashMapSpliterator<K,V>
+        implements Spliterator<Map.Entry<K,V>> {
+        EntrySpliterator(IdentityHashMap<K,V> m, int origin, int fence, int est,
+                         int expectedModCount) {
+            super(m, origin, fence, est, expectedModCount);
+        }
+
+        public EntrySpliterator<K,V> trySplit() {
+            int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1;
+            return (lo >= mid) ? null :
+                new EntrySpliterator<K,V>(map, lo, index = mid, est >>>= 1,
+                                          expectedModCount);
+        }
+
+        public void forEachRemaining(Consumer<? super Map.Entry<K, V>> action) {
+            if (action == null)
+                throw new NullPointerException();
+            int i, hi, mc;
+            IdentityHashMap<K,V> m; Object[] a;
+            if ((m = map) != null && (a = m.table) != null &&
+                (i = index) >= 0 && (index = hi = getFence()) <= a.length) {
+                for (; i < hi; i += 2) {
+                    Object key = a[i];
+                    if (key != null) {
+                        @SuppressWarnings("unchecked") K k =
+                            (K)unmaskNull(key);
+                        @SuppressWarnings("unchecked") V v = (V)a[i+1];
+                        action.accept
+                            (new AbstractMap.SimpleImmutableEntry<K,V>(k, v));
+
+                    }
+                }
+                if (m.modCount == expectedModCount)
+                    return;
+            }
+            throw new ConcurrentModificationException();
+        }
+
+        public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {
+            if (action == null)
+                throw new NullPointerException();
+            Object[] a = map.table;
+            int hi = getFence();
+            while (index < hi) {
+                Object key = a[index];
+                @SuppressWarnings("unchecked") V v = (V)a[index+1];
+                index += 2;
+                if (key != null) {
+                    @SuppressWarnings("unchecked") K k =
+                        (K)unmaskNull(key);
+                    action.accept
+                        (new AbstractMap.SimpleImmutableEntry<K,V>(k, v));
+                    if (map.modCount != expectedModCount)
+                        throw new ConcurrentModificationException();
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public int characteristics() {
+            return (fence < 0 || est == map.size ? SIZED : 0) | Spliterator.DISTINCT;
+        }
+    }
+
 }
diff --git a/ojluni/src/main/java/java/util/LinkedHashMap.java b/ojluni/src/main/java/java/util/LinkedHashMap.java
index c1b2644..ab89692 100755
--- a/ojluni/src/main/java/java/util/LinkedHashMap.java
+++ b/ojluni/src/main/java/java/util/LinkedHashMap.java
@@ -131,10 +131,20 @@
  * exception for its correctness:   <i>the fail-fast behavior of iterators
  * should be used only to detect bugs.</i>
  *
+ * <p>The spliterators returned by the spliterator method of the collections
+ * returned by all of this class's collection view methods are
+ * <em><a href="Spliterator.html#binding">late-binding</a></em>,
+ * <em>fail-fast</em>, and additionally report {@link Spliterator#ORDERED}.
+ *
  * <p>This class is a member of the
  * <a href="{@docRoot}/../technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
+ * @implNote
+ * The spliterators returned by the spliterator method of the collections
+ * returned by all of this class's collection view methods are created from
+ * the iterators of the corresponding collections.
+ *
  * @param <K> the type of keys maintained by this map
  * @param <V> the type of mapped values
  *
diff --git a/ojluni/src/main/java/java/util/LinkedHashSet.java b/ojluni/src/main/java/java/util/LinkedHashSet.java
index 399ba80..08606cf 100755
--- a/ojluni/src/main/java/java/util/LinkedHashSet.java
+++ b/ojluni/src/main/java/java/util/LinkedHashSet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -168,4 +168,28 @@
         super(Math.max(2*c.size(), 11), .75f, true);
         addAll(c);
     }
+
+    /**
+     * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
+     * and <em>fail-fast</em> {@code Spliterator} over the elements in this set.
+     *
+     * <p>The {@code Spliterator} reports {@link Spliterator#SIZED},
+     * {@link Spliterator#DISTINCT}, and {@code ORDERED}.  Implementations
+     * should document the reporting of additional characteristic values.
+     *
+     * @implNote
+     * The implementation creates a
+     * <em><a href="Spliterator.html#binding">late-binding</a></em> spliterator
+     * from the set's {@code Iterator}.  The spliterator inherits the
+     * <em>fail-fast</em> properties of the set's iterator.
+     * The created {@code Spliterator} additionally reports
+     * {@link Spliterator#SUBSIZED}.
+     *
+     * @return a {@code Spliterator} over the elements in this set
+     * @since 1.8
+     */
+    @Override
+    public Spliterator<E> spliterator() {
+        return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED);
+    }
 }
diff --git a/ojluni/src/main/java/java/util/LinkedList.java b/ojluni/src/main/java/java/util/LinkedList.java
index 43b9c4a..0236076 100755
--- a/ojluni/src/main/java/java/util/LinkedList.java
+++ b/ojluni/src/main/java/java/util/LinkedList.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -1008,7 +1008,7 @@
         try {
             return (LinkedList<E>) super.clone();
         } catch (CloneNotSupportedException e) {
-            throw new InternalError();
+            throw new InternalError(e);
         }
     }
 
@@ -1149,4 +1149,115 @@
         for (int i = 0; i < size; i++)
             linkLast((E)s.readObject());
     }
+
+    /**
+     * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
+     * and <em>fail-fast</em> {@link Spliterator} over the elements in this
+     * list.
+     *
+     * <p>The {@code Spliterator} reports {@link Spliterator#SIZED} and
+     * {@link Spliterator#ORDERED}.  Overriding implementations should document
+     * the reporting of additional characteristic values.
+     *
+     * @implNote
+     * The {@code Spliterator} additionally reports {@link Spliterator#SUBSIZED}
+     * and implements {@code trySplit} to permit limited parallelism..
+     *
+     * @return a {@code Spliterator} over the elements in this list
+     * @since 1.8
+     */
+    @Override
+    public Spliterator<E> spliterator() {
+        return new LLSpliterator<E>(this, -1, 0);
+    }
+
+    /** A customized variant of Spliterators.IteratorSpliterator */
+    static final class LLSpliterator<E> implements Spliterator<E> {
+        static final int BATCH_UNIT = 1 << 10;  // batch array size increment
+        static final int MAX_BATCH = 1 << 25;  // max batch array size;
+        final LinkedList<E> list; // null OK unless traversed
+        Node<E> current;      // current node; null until initialized
+        int est;              // size estimate; -1 until first needed
+        int expectedModCount; // initialized when est set
+        int batch;            // batch size for splits
+
+        LLSpliterator(LinkedList<E> list, int est, int expectedModCount) {
+            this.list = list;
+            this.est = est;
+            this.expectedModCount = expectedModCount;
+        }
+
+        final int getEst() {
+            int s; // force initialization
+            final LinkedList<E> lst;
+            if ((s = est) < 0) {
+                if ((lst = list) == null)
+                    s = est = 0;
+                else {
+                    expectedModCount = lst.modCount;
+                    current = lst.first;
+                    s = est = lst.size;
+                }
+            }
+            return s;
+        }
+
+        public long estimateSize() { return (long) getEst(); }
+
+        public Spliterator<E> trySplit() {
+            Node<E> p;
+            int s = getEst();
+            if (s > 1 && (p = current) != null) {
+                int n = batch + BATCH_UNIT;
+                if (n > s)
+                    n = s;
+                if (n > MAX_BATCH)
+                    n = MAX_BATCH;
+                Object[] a = new Object[n];
+                int j = 0;
+                do { a[j++] = p.item; } while ((p = p.next) != null && j < n);
+                current = p;
+                batch = j;
+                est = s - j;
+                return Spliterators.spliterator(a, 0, j, Spliterator.ORDERED);
+            }
+            return null;
+        }
+
+        public void forEachRemaining(Consumer<? super E> action) {
+            Node<E> p; int n;
+            if (action == null) throw new NullPointerException();
+            if ((n = getEst()) > 0 && (p = current) != null) {
+                current = null;
+                est = 0;
+                do {
+                    E e = p.item;
+                    p = p.next;
+                    action.accept(e);
+                } while (p != null && --n > 0);
+            }
+            if (list.modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+        }
+
+        public boolean tryAdvance(Consumer<? super E> action) {
+            Node<E> p;
+            if (action == null) throw new NullPointerException();
+            if (getEst() > 0 && (p = current) != null) {
+                --est;
+                E e = p.item;
+                current = p.next;
+                action.accept(e);
+                if (list.modCount != expectedModCount)
+                    throw new ConcurrentModificationException();
+                return true;
+            }
+            return false;
+        }
+
+        public int characteristics() {
+            return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
+        }
+    }
+
 }
diff --git a/ojluni/src/main/java/java/util/List.java b/ojluni/src/main/java/java/util/List.java
index d3d2a1b..070e46d 100755
--- a/ojluni/src/main/java/java/util/List.java
+++ b/ojluni/src/main/java/java/util/List.java
@@ -597,4 +597,29 @@
      *         fromIndex &gt; toIndex</tt>)
      */
     List<E> subList(int fromIndex, int toIndex);
+
+    /**
+     * Creates a {@link Spliterator} over the elements in this list.
+     *
+     * <p>The {@code Spliterator} reports {@link Spliterator#SIZED} and
+     * {@link Spliterator#ORDERED}.  Implementations should document the
+     * reporting of additional characteristic values.
+     *
+     * @implSpec
+     * The default implementation creates a
+     * <em><a href="Spliterator.html#binding">late-binding</a></em> spliterator
+     * from the list's {@code Iterator}.  The spliterator inherits the
+     * <em>fail-fast</em> properties of the list's iterator.
+     *
+     * @implNote
+     * The created {@code Spliterator} additionally reports
+     * {@link Spliterator#SUBSIZED}.
+     *
+     * @return a {@code Spliterator} over the elements in this list
+     * @since 1.8
+     */
+    @Override
+    default Spliterator<E> spliterator() {
+        return Spliterators.spliterator(this, Spliterator.ORDERED);
+    }
 }
diff --git a/ojluni/src/main/java/java/util/Map.java b/ojluni/src/main/java/java/util/Map.java
index afb24ba..f59b5a9 100755
--- a/ojluni/src/main/java/java/util/Map.java
+++ b/ojluni/src/main/java/java/util/Map.java
@@ -26,6 +26,7 @@
 package java.util;
 
 
+import java.io.Serializable;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.util.function.Function;
@@ -448,6 +449,42 @@
          * @see #equals(Object)
          */
         int hashCode();
+
+        /**
+         * Returns a comparator that compares {@link Map.Entry} in natural order on key.
+         *
+         * <p>The returned comparator is serializable and throws {@link
+         * NullPointerException} when comparing an entry with a null key.
+         *
+         * @param  <K> the {@link Comparable} type of then map keys
+         * @param  <V> the type of the map values
+         * @return a comparator that compares {@link Map.Entry} in natural order on key.
+         * @see Comparable
+         * @since 1.8
+         */
+        public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
+            return (Comparator<Map.Entry<K, V>> & Serializable)
+                (c1, c2) -> c1.getKey().compareTo(c2.getKey());
+        }
+
+        /**
+         * Returns a comparator that compares {@link Map.Entry} by key using the given
+         * {@link Comparator}.
+         *
+         * <p>The returned comparator is serializable if the specified comparator
+         * is also serializable.
+         *
+         * @param  <K> the type of the map keys
+         * @param  <V> the type of the map values
+         * @param  cmp the key {@link Comparator}
+         * @return a comparator that compares {@link Map.Entry} by the key.
+         * @since 1.8
+         */
+        public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
+            Objects.requireNonNull(cmp);
+            return (Comparator<Map.Entry<K, V>> & Serializable)
+                (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
+        }
     }
 
     // Comparison and hashing
diff --git a/ojluni/src/main/java/java/util/PriorityQueue.java b/ojluni/src/main/java/java/util/PriorityQueue.java
index b4416ab..b42647e 100755
--- a/ojluni/src/main/java/java/util/PriorityQueue.java
+++ b/ojluni/src/main/java/java/util/PriorityQueue.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,8 @@
 
 package java.util;
 
+import java.util.function.Consumer;
+
 /**
  * An unbounded priority {@linkplain Queue queue} based on a priority heap.
  * The elements of the priority queue are ordered according to their
@@ -56,14 +58,14 @@
  * the priority queue in any particular order. If you need ordered
  * traversal, consider using {@code Arrays.sort(pq.toArray())}.
  *
- * <p> <strong>Note that this implementation is not synchronized.</strong>
+ * <p><strong>Note that this implementation is not synchronized.</strong>
  * Multiple threads should not access a {@code PriorityQueue}
  * instance concurrently if any of the threads modifies the queue.
  * Instead, use the thread-safe {@link
  * java.util.concurrent.PriorityBlockingQueue} class.
  *
  * <p>Implementation note: this implementation provides
- * O(log(n)) time for the enqueing and dequeing methods
+ * O(log(n)) time for the enqueuing and dequeuing methods
  * ({@code offer}, {@code poll}, {@code remove()} and {@code add});
  * linear time for the {@code remove(Object)} and {@code contains(Object)}
  * methods; and constant time for the retrieval methods
@@ -92,7 +94,7 @@
      * heap and each descendant d of n, n <= d.  The element with the
      * lowest value is in queue[0], assuming the queue is nonempty.
      */
-    private transient Object[] queue;
+    transient Object[] queue; // non-private to simplify nested class access
 
     /**
      * The number of elements in the priority queue.
@@ -109,7 +111,7 @@
      * The number of times this priority queue has been
      * <i>structurally modified</i>.  See AbstractList for gory details.
      */
-    private transient int modCount = 0;
+    transient int modCount = 0; // non-private to simplify nested class access
 
     /**
      * Creates a {@code PriorityQueue} with the default initial
@@ -134,6 +136,19 @@
     }
 
     /**
+     * Creates a {@code PriorityQueue} with the default initial capacity and
+     * whose elements are ordered according to the specified comparator.
+     *
+     * @param  comparator the comparator that will be used to order this
+     *         priority queue.  If {@code null}, the {@linkplain Comparable
+     *         natural ordering} of the elements will be used.
+     * @since 1.8
+     */
+    public PriorityQueue(Comparator<? super E> comparator) {
+        this(DEFAULT_INITIAL_CAPACITY, comparator);
+    }
+
+    /**
      * Creates a {@code PriorityQueue} with the specified initial capacity
      * that orders its elements according to the specified comparator.
      *
@@ -330,10 +345,9 @@
         return true;
     }
 
+    @SuppressWarnings("unchecked")
     public E peek() {
-        if (size == 0)
-            return null;
-        return (E) queue[0];
+        return (size == 0) ? null : (E) queue[0];
     }
 
     private int indexOf(Object o) {
@@ -430,15 +444,14 @@
      * precise control over the runtime type of the output array, and may,
      * under certain circumstances, be used to save allocation costs.
      *
-     * <p>Suppose <tt>x</tt> is a queue known to contain only strings.
+     * <p>Suppose {@code x} is a queue known to contain only strings.
      * The following code can be used to dump the queue into a newly
-     * allocated array of <tt>String</tt>:
+     * allocated array of {@code String}:
      *
-     * <pre>
-     *     String[] y = x.toArray(new String[0]);</pre>
+     *  <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
      *
-     * Note that <tt>toArray(new Object[0])</tt> is identical in function to
-     * <tt>toArray()</tt>.
+     * Note that {@code toArray(new Object[0])} is identical in function to
+     * {@code toArray()}.
      *
      * @param a the array into which the elements of the queue are to
      *          be stored, if it is big enough; otherwise, a new array of the
@@ -449,7 +462,9 @@
      *         this queue
      * @throws NullPointerException if the specified array is null
      */
+    @SuppressWarnings("unchecked")
     public <T> T[] toArray(T[] a) {
+        final int size = this.size;
         if (a.length < size)
             // Make a new array of a's runtime type, but my contents:
             return (T[]) Arrays.copyOf(queue, size, a.getClass());
@@ -514,6 +529,7 @@
                 (forgetMeNot != null && !forgetMeNot.isEmpty());
         }
 
+        @SuppressWarnings("unchecked")
         public E next() {
             if (expectedModCount != modCount)
                 throw new ConcurrentModificationException();
@@ -566,6 +582,7 @@
         size = 0;
     }
 
+    @SuppressWarnings("unchecked")
     public E poll() {
         if (size == 0)
             return null;
@@ -591,6 +608,7 @@
      * position before i. This fact is used by iterator.remove so as to
      * avoid missing traversing elements.
      */
+    @SuppressWarnings("unchecked")
     private E removeAt(int i) {
         assert i >= 0 && i < size;
         modCount++;
@@ -629,6 +647,7 @@
             siftUpComparable(k, x);
     }
 
+    @SuppressWarnings("unchecked")
     private void siftUpComparable(int k, E x) {
         Comparable<? super E> key = (Comparable<? super E>) x;
         while (k > 0) {
@@ -642,6 +661,7 @@
         queue[k] = key;
     }
 
+    @SuppressWarnings("unchecked")
     private void siftUpUsingComparator(int k, E x) {
         while (k > 0) {
             int parent = (k - 1) >>> 1;
@@ -669,6 +689,7 @@
             siftDownComparable(k, x);
     }
 
+    @SuppressWarnings("unchecked")
     private void siftDownComparable(int k, E x) {
         Comparable<? super E> key = (Comparable<? super E>)x;
         int half = size >>> 1;        // loop while a non-leaf
@@ -687,6 +708,7 @@
         queue[k] = key;
     }
 
+    @SuppressWarnings("unchecked")
     private void siftDownUsingComparator(int k, E x) {
         int half = size >>> 1;
         while (k < half) {
@@ -708,6 +730,7 @@
      * Establishes the heap invariant (described above) in the entire tree,
      * assuming nothing about the order of the elements prior to the call.
      */
+    @SuppressWarnings("unchecked")
     private void heapify() {
         for (int i = (size >>> 1) - 1; i >= 0; i--)
             siftDown(i, (E) queue[i]);
@@ -727,8 +750,7 @@
     }
 
     /**
-     * Saves the state of the instance to a stream (that
-     * is, serializes it).
+     * Saves this queue to a stream (that is, serializes it).
      *
      * @serialData The length of the array backing the instance is
      *             emitted (int), followed by all of its elements
@@ -736,7 +758,7 @@
      * @param s the stream
      */
     private void writeObject(java.io.ObjectOutputStream s)
-        throws java.io.IOException{
+        throws java.io.IOException {
         // Write out element count, and any hidden stuff
         s.defaultWriteObject();
 
@@ -772,4 +794,112 @@
         // spec has never explained what that might be.
         heapify();
     }
+
+    /**
+     * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
+     * and <em>fail-fast</em> {@link Spliterator} over the elements in this
+     * queue.
+     *
+     * <p>The {@code Spliterator} reports {@link Spliterator#SIZED},
+     * {@link Spliterator#SUBSIZED}, and {@link Spliterator#NONNULL}.
+     * Overriding implementations should document the reporting of additional
+     * characteristic values.
+     *
+     * @return a {@code Spliterator} over the elements in this queue
+     * @since 1.8
+     */
+    public final Spliterator<E> spliterator() {
+        return new PriorityQueueSpliterator<E>(this, 0, -1, 0);
+    }
+
+    static final class PriorityQueueSpliterator<E> implements Spliterator<E> {
+        /*
+         * This is very similar to ArrayList Spliterator, except for
+         * extra null checks.
+         */
+        private final PriorityQueue<E> pq;
+        private int index;            // current index, modified on advance/split
+        private int fence;            // -1 until first use
+        private int expectedModCount; // initialized when fence set
+
+        /** Creates new spliterator covering the given range */
+        PriorityQueueSpliterator(PriorityQueue<E> pq, int origin, int fence,
+                             int expectedModCount) {
+            this.pq = pq;
+            this.index = origin;
+            this.fence = fence;
+            this.expectedModCount = expectedModCount;
+        }
+
+        private int getFence() { // initialize fence to size on first use
+            int hi;
+            if ((hi = fence) < 0) {
+                expectedModCount = pq.modCount;
+                hi = fence = pq.size;
+            }
+            return hi;
+        }
+
+        public PriorityQueueSpliterator<E> trySplit() {
+            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
+            return (lo >= mid) ? null :
+                new PriorityQueueSpliterator<E>(pq, lo, index = mid,
+                                                expectedModCount);
+        }
+
+        @SuppressWarnings("unchecked")
+        public void forEachRemaining(Consumer<? super E> action) {
+            int i, hi, mc; // hoist accesses and checks from loop
+            PriorityQueue<E> q; Object[] a;
+            if (action == null)
+                throw new NullPointerException();
+            if ((q = pq) != null && (a = q.queue) != null) {
+                if ((hi = fence) < 0) {
+                    mc = q.modCount;
+                    hi = q.size;
+                }
+                else
+                    mc = expectedModCount;
+                if ((i = index) >= 0 && (index = hi) <= a.length) {
+                    for (E e;; ++i) {
+                        if (i < hi) {
+                            if ((e = (E) a[i]) == null) // must be CME
+                                break;
+                            action.accept(e);
+                        }
+                        else if (q.modCount != mc)
+                            break;
+                        else
+                            return;
+                    }
+                }
+            }
+            throw new ConcurrentModificationException();
+        }
+
+        public boolean tryAdvance(Consumer<? super E> action) {
+            if (action == null)
+                throw new NullPointerException();
+            int hi = getFence(), lo = index;
+            if (lo >= 0 && lo < hi) {
+                index = lo + 1;
+                @SuppressWarnings("unchecked") E e = (E)pq.queue[lo];
+                if (e == null)
+                    throw new ConcurrentModificationException();
+                action.accept(e);
+                if (pq.modCount != expectedModCount)
+                    throw new ConcurrentModificationException();
+                return true;
+            }
+            return false;
+        }
+
+        public long estimateSize() {
+            return (long) (getFence() - index);
+        }
+
+        public int characteristics() {
+            return Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.NONNULL;
+        }
+    }
 }
diff --git a/ojluni/src/main/java/java/util/Set.java b/ojluni/src/main/java/java/util/Set.java
index 004e970..2703049 100755
--- a/ojluni/src/main/java/java/util/Set.java
+++ b/ojluni/src/main/java/java/util/Set.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -382,4 +382,32 @@
      * @see Set#equals(Object)
      */
     int hashCode();
+
+    /**
+     * Creates a {@code Spliterator} over the elements in this set.
+     *
+     * <p>The {@code Spliterator} reports {@link Spliterator#DISTINCT}.
+     * Implementations should document the reporting of additional
+     * characteristic values.
+     *
+     * @implSpec
+     * The default implementation creates a
+     * <em><a href="Spliterator.html#binding">late-binding</a></em> spliterator
+     * from the set's {@code Iterator}.  The spliterator inherits the
+     * <em>fail-fast</em> properties of the set's iterator.
+     * <p>
+     * The created {@code Spliterator} additionally reports
+     * {@link Spliterator#SIZED}.
+     *
+     * @implNote
+     * The created {@code Spliterator} additionally reports
+     * {@link Spliterator#SUBSIZED}.
+     *
+     * @return a {@code Spliterator} over the elements in this set
+     * @since 1.8
+     */
+    @Override
+    default Spliterator<E> spliterator() {
+        return Spliterators.spliterator(this, Spliterator.DISTINCT);
+    }
 }
diff --git a/ojluni/src/main/java/java/util/SortedSet.java b/ojluni/src/main/java/java/util/SortedSet.java
index ad04e98..3ea9329 100755
--- a/ojluni/src/main/java/java/util/SortedSet.java
+++ b/ojluni/src/main/java/java/util/SortedSet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -219,4 +219,46 @@
      * @throws NoSuchElementException if this set is empty
      */
     E last();
+
+    /**
+     * Creates a {@code Spliterator} over the elements in this sorted set.
+     *
+     * <p>The {@code Spliterator} reports {@link Spliterator#DISTINCT},
+     * {@link Spliterator#SORTED} and {@link Spliterator#ORDERED}.
+     * Implementations should document the reporting of additional
+     * characteristic values.
+     *
+     * <p>The spliterator's comparator (see
+     * {@link java.util.Spliterator#getComparator()}) must be {@code null} if
+     * the sorted set's comparator (see {@link #comparator()}) is {@code null}.
+     * Otherwise, the spliterator's comparator must be the same as or impose the
+     * same total ordering as the sorted set's comparator.
+     *
+     * @implSpec
+     * The default implementation creates a
+     * <em><a href="Spliterator.html#binding">late-binding</a></em> spliterator
+     * from the sorted set's {@code Iterator}.  The spliterator inherits the
+     * <em>fail-fast</em> properties of the set's iterator.  The
+     * spliterator's comparator is the same as the sorted set's comparator.
+     * <p>
+     * The created {@code Spliterator} additionally reports
+     * {@link Spliterator#SIZED}.
+     *
+     * @implNote
+     * The created {@code Spliterator} additionally reports
+     * {@link Spliterator#SUBSIZED}.
+     *
+     * @return a {@code Spliterator} over the elements in this sorted set
+     * @since 1.8
+     */
+    @Override
+    default Spliterator<E> spliterator() {
+        return new Spliterators.IteratorSpliterator<E>(
+                this, Spliterator.DISTINCT | Spliterator.SORTED | Spliterator.ORDERED) {
+            @Override
+            public Comparator<? super E> getComparator() {
+                return SortedSet.this.comparator();
+            }
+        };
+    }
 }
diff --git a/ojluni/src/main/java/java/util/Spliterator.java b/ojluni/src/main/java/java/util/Spliterator.java
new file mode 100644
index 0000000..18cb082
--- /dev/null
+++ b/ojluni/src/main/java/java/util/Spliterator.java
@@ -0,0 +1,832 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+/**
+ * An object for traversing and partitioning elements of a source.  The source
+ * of elements covered by a Spliterator could be, for example, an array, a
+ * {@link Collection}, an IO channel, or a generator function.
+ *
+ * <p>A Spliterator may traverse elements individually ({@link
+ * #tryAdvance tryAdvance()}) or sequentially in bulk
+ * ({@link #forEachRemaining forEachRemaining()}).
+ *
+ * <p>A Spliterator may also partition off some of its elements (using
+ * {@link #trySplit}) as another Spliterator, to be used in
+ * possibly-parallel operations.  Operations using a Spliterator that
+ * cannot split, or does so in a highly imbalanced or inefficient
+ * manner, are unlikely to benefit from parallelism.  Traversal
+ * and splitting exhaust elements; each Spliterator is useful for only a single
+ * bulk computation.
+ *
+ * <p>A Spliterator also reports a set of {@link #characteristics()} of its
+ * structure, source, and elements from among {@link #ORDERED},
+ * {@link #DISTINCT}, {@link #SORTED}, {@link #SIZED}, {@link #NONNULL},
+ * {@link #IMMUTABLE}, {@link #CONCURRENT}, and {@link #SUBSIZED}. These may
+ * be employed by Spliterator clients to control, specialize or simplify
+ * computation.  For example, a Spliterator for a {@link Collection} would
+ * report {@code SIZED}, a Spliterator for a {@link Set} would report
+ * {@code DISTINCT}, and a Spliterator for a {@link SortedSet} would also
+ * report {@code SORTED}.  Characteristics are reported as a simple unioned bit
+ * set.
+ *
+ * Some characteristics additionally constrain method behavior; for example if
+ * {@code ORDERED}, traversal methods must conform to their documented ordering.
+ * New characteristics may be defined in the future, so implementors should not
+ * assign meanings to unlisted values.
+ *
+ * <p><a name="binding">A Spliterator that does not report {@code IMMUTABLE} or
+ * {@code CONCURRENT} is expected to have a documented policy concerning:
+ * when the spliterator <em>binds</em> to the element source; and detection of
+ * structural interference of the element source detected after binding.</a>  A
+ * <em>late-binding</em> Spliterator binds to the source of elements at the
+ * point of first traversal, first split, or first query for estimated size,
+ * rather than at the time the Spliterator is created.  A Spliterator that is
+ * not <em>late-binding</em> binds to the source of elements at the point of
+ * construction or first invocation of any method.  Modifications made to the
+ * source prior to binding are reflected when the Spliterator is traversed.
+ * After binding a Spliterator should, on a best-effort basis, throw
+ * {@link ConcurrentModificationException} if structural interference is
+ * detected.  Spliterators that do this are called <em>fail-fast</em>.  The
+ * bulk traversal method ({@link #forEachRemaining forEachRemaining()}) of a
+ * Spliterator may optimize traversal and check for structural interference
+ * after all elements have been traversed, rather than checking per-element and
+ * failing immediately.
+ *
+ * <p>Spliterators can provide an estimate of the number of remaining elements
+ * via the {@link #estimateSize} method.  Ideally, as reflected in characteristic
+ * {@link #SIZED}, this value corresponds exactly to the number of elements
+ * that would be encountered in a successful traversal.  However, even when not
+ * exactly known, an estimated value value may still be useful to operations
+ * being performed on the source, such as helping to determine whether it is
+ * preferable to split further or traverse the remaining elements sequentially.
+ *
+ * <p>Despite their obvious utility in parallel algorithms, spliterators are not
+ * expected to be thread-safe; instead, implementations of parallel algorithms
+ * using spliterators should ensure that the spliterator is only used by one
+ * thread at a time.  This is generally easy to attain via <em>serial
+ * thread-confinement</em>, which often is a natural consequence of typical
+ * parallel algorithms that work by recursive decomposition.  A thread calling
+ * {@link #trySplit()} may hand over the returned Spliterator to another thread,
+ * which in turn may traverse or further split that Spliterator.  The behaviour
+ * of splitting and traversal is undefined if two or more threads operate
+ * concurrently on the same spliterator.  If the original thread hands a
+ * spliterator off to another thread for processing, it is best if that handoff
+ * occurs before any elements are consumed with {@link #tryAdvance(Consumer)
+ * tryAdvance()}, as certain guarantees (such as the accuracy of
+ * {@link #estimateSize()} for {@code SIZED} spliterators) are only valid before
+ * traversal has begun.
+ *
+ * <p>Primitive subtype specializations of {@code Spliterator} are provided for
+ * {@link OfInt int}, {@link OfLong long}, and {@link OfDouble double} values.
+ * The subtype default implementations of
+ * {@link Spliterator#tryAdvance(java.util.function.Consumer)}
+ * and {@link Spliterator#forEachRemaining(java.util.function.Consumer)} box
+ * primitive values to instances of their corresponding wrapper class.  Such
+ * boxing may undermine any performance advantages gained by using the primitive
+ * specializations.  To avoid boxing, the corresponding primitive-based methods
+ * should be used.  For example,
+ * {@link Spliterator.OfInt#tryAdvance(java.util.function.IntConsumer)}
+ * and {@link Spliterator.OfInt#forEachRemaining(java.util.function.IntConsumer)}
+ * should be used in preference to
+ * {@link Spliterator.OfInt#tryAdvance(java.util.function.Consumer)} and
+ * {@link Spliterator.OfInt#forEachRemaining(java.util.function.Consumer)}.
+ * Traversal of primitive values using boxing-based methods
+ * {@link #tryAdvance tryAdvance()} and
+ * {@link #forEachRemaining(java.util.function.Consumer) forEachRemaining()}
+ * does not affect the order in which the values, transformed to boxed values,
+ * are encountered.
+ *
+ * @apiNote
+ * <p>Spliterators, like {@code Iterators}s, are for traversing the elements of
+ * a source.  The {@code Spliterator} API was designed to support efficient
+ * parallel traversal in addition to sequential traversal, by supporting
+ * decomposition as well as single-element iteration.  In addition, the
+ * protocol for accessing elements via a Spliterator is designed to impose
+ * smaller per-element overhead than {@code Iterator}, and to avoid the inherent
+ * race involved in having separate methods for {@code hasNext()} and
+ * {@code next()}.
+ *
+ * <p>For mutable sources, arbitrary and non-deterministic behavior may occur if
+ * the source is structurally interfered with (elements added, replaced, or
+ * removed) between the time that the Spliterator binds to its data source and
+ * the end of traversal.  For example, such interference will produce arbitrary,
+ * non-deterministic results when using the {@code java.util.stream} framework.
+ *
+ * <p>Structural interference of a source can be managed in the following ways
+ * (in approximate order of decreasing desirability):
+ * <ul>
+ * <li>The source cannot be structurally interfered with.
+ * <br>For example, an instance of
+ * {@link java.util.concurrent.CopyOnWriteArrayList} is an immutable source.
+ * A Spliterator created from the source reports a characteristic of
+ * {@code IMMUTABLE}.</li>
+ * <li>The source manages concurrent modifications.
+ * <br>For example, a key set of a {@link java.util.concurrent.ConcurrentHashMap}
+ * is a concurrent source.  A Spliterator created from the source reports a
+ * characteristic of {@code CONCURRENT}.</li>
+ * <li>The mutable source provides a late-binding and fail-fast Spliterator.
+ * <br>Late binding narrows the window during which interference can affect
+ * the calculation; fail-fast detects, on a best-effort basis, that structural
+ * interference has occurred after traversal has commenced and throws
+ * {@link ConcurrentModificationException}.  For example, {@link ArrayList},
+ * and many other non-concurrent {@code Collection} classes in the JDK, provide
+ * a late-binding, fail-fast spliterator.</li>
+ * <li>The mutable source provides a non-late-binding but fail-fast Spliterator.
+ * <br>The source increases the likelihood of throwing
+ * {@code ConcurrentModificationException} since the window of potential
+ * interference is larger.</li>
+ * <li>The mutable source provides a late-binding and non-fail-fast Spliterator.
+ * <br>The source risks arbitrary, non-deterministic behavior after traversal
+ * has commenced since interference is not detected.
+ * </li>
+ * <li>The mutable source provides a non-late-binding and non-fail-fast
+ * Spliterator.
+ * <br>The source increases the risk of arbitrary, non-deterministic behavior
+ * since non-detected interference may occur after construction.
+ * </li>
+ * </ul>
+ *
+ * <p><b>Example.</b> Here is a class (not a very useful one, except
+ * for illustration) that maintains an array in which the actual data
+ * are held in even locations, and unrelated tag data are held in odd
+ * locations. Its Spliterator ignores the tags.
+ *
+ * <pre> {@code
+ * class TaggedArray<T> {
+ *   private final Object[] elements; // immutable after construction
+ *   TaggedArray(T[] data, Object[] tags) {
+ *     int size = data.length;
+ *     if (tags.length != size) throw new IllegalArgumentException();
+ *     this.elements = new Object[2 * size];
+ *     for (int i = 0, j = 0; i < size; ++i) {
+ *       elements[j++] = data[i];
+ *       elements[j++] = tags[i];
+ *     }
+ *   }
+ *
+ *   public Spliterator<T> spliterator() {
+ *     return new TaggedArraySpliterator<>(elements, 0, elements.length);
+ *   }
+ *
+ *   static class TaggedArraySpliterator<T> implements Spliterator<T> {
+ *     private final Object[] array;
+ *     private int origin; // current index, advanced on split or traversal
+ *     private final int fence; // one past the greatest index
+ *
+ *     TaggedArraySpliterator(Object[] array, int origin, int fence) {
+ *       this.array = array; this.origin = origin; this.fence = fence;
+ *     }
+ *
+ *     public void forEachRemaining(Consumer<? super T> action) {
+ *       for (; origin < fence; origin += 2)
+ *         action.accept((T) array[origin]);
+ *     }
+ *
+ *     public boolean tryAdvance(Consumer<? super T> action) {
+ *       if (origin < fence) {
+ *         action.accept((T) array[origin]);
+ *         origin += 2;
+ *         return true;
+ *       }
+ *       else // cannot advance
+ *         return false;
+ *     }
+ *
+ *     public Spliterator<T> trySplit() {
+ *       int lo = origin; // divide range in half
+ *       int mid = ((lo + fence) >>> 1) & ~1; // force midpoint to be even
+ *       if (lo < mid) { // split out left half
+ *         origin = mid; // reset this Spliterator's origin
+ *         return new TaggedArraySpliterator<>(array, lo, mid);
+ *       }
+ *       else       // too small to split
+ *         return null;
+ *     }
+ *
+ *     public long estimateSize() {
+ *       return (long)((fence - origin) / 2);
+ *     }
+ *
+ *     public int characteristics() {
+ *       return ORDERED | SIZED | IMMUTABLE | SUBSIZED;
+ *     }
+ *   }
+ * }}</pre>
+ *
+ * <p>As an example how a parallel computation framework, such as the
+ * {@code java.util.stream} package, would use Spliterator in a parallel
+ * computation, here is one way to implement an associated parallel forEach,
+ * that illustrates the primary usage idiom of splitting off subtasks until
+ * the estimated amount of work is small enough to perform
+ * sequentially. Here we assume that the order of processing across
+ * subtasks doesn't matter; different (forked) tasks may further split
+ * and process elements concurrently in undetermined order.  This
+ * example uses a {@link java.util.concurrent.CountedCompleter};
+ * similar usages apply to other parallel task constructions.
+ *
+ * <pre>{@code
+ * static <T> void parEach(TaggedArray<T> a, Consumer<T> action) {
+ *   Spliterator<T> s = a.spliterator();
+ *   long targetBatchSize = s.estimateSize() / (ForkJoinPool.getCommonPoolParallelism() * 8);
+ *   new ParEach(null, s, action, targetBatchSize).invoke();
+ * }
+ *
+ * static class ParEach<T> extends CountedCompleter<Void> {
+ *   final Spliterator<T> spliterator;
+ *   final Consumer<T> action;
+ *   final long targetBatchSize;
+ *
+ *   ParEach(ParEach<T> parent, Spliterator<T> spliterator,
+ *           Consumer<T> action, long targetBatchSize) {
+ *     super(parent);
+ *     this.spliterator = spliterator; this.action = action;
+ *     this.targetBatchSize = targetBatchSize;
+ *   }
+ *
+ *   public void compute() {
+ *     Spliterator<T> sub;
+ *     while (spliterator.estimateSize() > targetBatchSize &&
+ *            (sub = spliterator.trySplit()) != null) {
+ *       addToPendingCount(1);
+ *       new ParEach<>(this, sub, action, targetBatchSize).fork();
+ *     }
+ *     spliterator.forEachRemaining(action);
+ *     propagateCompletion();
+ *   }
+ * }}</pre>
+ *
+ * @implNote
+ * If the boolean system property {@code org.openjdk.java.util.stream.tripwire}
+ * is set to {@code true} then diagnostic warnings are reported if boxing of
+ * primitive values occur when operating on primitive subtype specializations.
+ *
+ * @param <T> the type of elements returned by this Spliterator
+ *
+ * @see Collection
+ * @since 1.8
+ */
+public interface Spliterator<T> {
+    /**
+     * If a remaining element exists, performs the given action on it,
+     * returning {@code true}; else returns {@code false}.  If this
+     * Spliterator is {@link #ORDERED} the action is performed on the
+     * next element in encounter order.  Exceptions thrown by the
+     * action are relayed to the caller.
+     *
+     * @param action The action
+     * @return {@code false} if no remaining elements existed
+     * upon entry to this method, else {@code true}.
+     * @throws NullPointerException if the specified action is null
+     */
+    boolean tryAdvance(Consumer<? super T> action);
+
+    /**
+     * Performs the given action for each remaining element, sequentially in
+     * the current thread, until all elements have been processed or the action
+     * throws an exception.  If this Spliterator is {@link #ORDERED}, actions
+     * are performed in encounter order.  Exceptions thrown by the action
+     * are relayed to the caller.
+     *
+     * @implSpec
+     * The default implementation repeatedly invokes {@link #tryAdvance} until
+     * it returns {@code false}.  It should be overridden whenever possible.
+     *
+     * @param action The action
+     * @throws NullPointerException if the specified action is null
+     */
+    default void forEachRemaining(Consumer<? super T> action) {
+        do { } while (tryAdvance(action));
+    }
+
+    /**
+     * If this spliterator can be partitioned, returns a Spliterator
+     * covering elements, that will, upon return from this method, not
+     * be covered by this Spliterator.
+     *
+     * <p>If this Spliterator is {@link #ORDERED}, the returned Spliterator
+     * must cover a strict prefix of the elements.
+     *
+     * <p>Unless this Spliterator covers an infinite number of elements,
+     * repeated calls to {@code trySplit()} must eventually return {@code null}.
+     * Upon non-null return:
+     * <ul>
+     * <li>the value reported for {@code estimateSize()} before splitting,
+     * must, after splitting, be greater than or equal to {@code estimateSize()}
+     * for this and the returned Spliterator; and</li>
+     * <li>if this Spliterator is {@code SUBSIZED}, then {@code estimateSize()}
+     * for this spliterator before splitting must be equal to the sum of
+     * {@code estimateSize()} for this and the returned Spliterator after
+     * splitting.</li>
+     * </ul>
+     *
+     * <p>This method may return {@code null} for any reason,
+     * including emptiness, inability to split after traversal has
+     * commenced, data structure constraints, and efficiency
+     * considerations.
+     *
+     * @apiNote
+     * An ideal {@code trySplit} method efficiently (without
+     * traversal) divides its elements exactly in half, allowing
+     * balanced parallel computation.  Many departures from this ideal
+     * remain highly effective; for example, only approximately
+     * splitting an approximately balanced tree, or for a tree in
+     * which leaf nodes may contain either one or two elements,
+     * failing to further split these nodes.  However, large
+     * deviations in balance and/or overly inefficient {@code
+     * trySplit} mechanics typically result in poor parallel
+     * performance.
+     *
+     * @return a {@code Spliterator} covering some portion of the
+     * elements, or {@code null} if this spliterator cannot be split
+     */
+    Spliterator<T> trySplit();
+
+    /**
+     * Returns an estimate of the number of elements that would be
+     * encountered by a {@link #forEachRemaining} traversal, or returns {@link
+     * Long#MAX_VALUE} if infinite, unknown, or too expensive to compute.
+     *
+     * <p>If this Spliterator is {@link #SIZED} and has not yet been partially
+     * traversed or split, or this Spliterator is {@link #SUBSIZED} and has
+     * not yet been partially traversed, this estimate must be an accurate
+     * count of elements that would be encountered by a complete traversal.
+     * Otherwise, this estimate may be arbitrarily inaccurate, but must decrease
+     * as specified across invocations of {@link #trySplit}.
+     *
+     * @apiNote
+     * Even an inexact estimate is often useful and inexpensive to compute.
+     * For example, a sub-spliterator of an approximately balanced binary tree
+     * may return a value that estimates the number of elements to be half of
+     * that of its parent; if the root Spliterator does not maintain an
+     * accurate count, it could estimate size to be the power of two
+     * corresponding to its maximum depth.
+     *
+     * @return the estimated size, or {@code Long.MAX_VALUE} if infinite,
+     *         unknown, or too expensive to compute.
+     */
+    long estimateSize();
+
+    /**
+     * Convenience method that returns {@link #estimateSize()} if this
+     * Spliterator is {@link #SIZED}, else {@code -1}.
+     * @implSpec
+     * The default implementation returns the result of {@code estimateSize()}
+     * if the Spliterator reports a characteristic of {@code SIZED}, and
+     * {@code -1} otherwise.
+     *
+     * @return the exact size, if known, else {@code -1}.
+     */
+    default long getExactSizeIfKnown() {
+        return (characteristics() & SIZED) == 0 ? -1L : estimateSize();
+    }
+
+    /**
+     * Returns a set of characteristics of this Spliterator and its
+     * elements. The result is represented as ORed values from {@link
+     * #ORDERED}, {@link #DISTINCT}, {@link #SORTED}, {@link #SIZED},
+     * {@link #NONNULL}, {@link #IMMUTABLE}, {@link #CONCURRENT},
+     * {@link #SUBSIZED}.  Repeated calls to {@code characteristics()} on
+     * a given spliterator, prior to or in-between calls to {@code trySplit},
+     * should always return the same result.
+     *
+     * <p>If a Spliterator reports an inconsistent set of
+     * characteristics (either those returned from a single invocation
+     * or across multiple invocations), no guarantees can be made
+     * about any computation using this Spliterator.
+     *
+     * @apiNote The characteristics of a given spliterator before splitting
+     * may differ from the characteristics after splitting.  For specific
+     * examples see the characteristic values {@link #SIZED}, {@link #SUBSIZED}
+     * and {@link #CONCURRENT}.
+     *
+     * @return a representation of characteristics
+     */
+    int characteristics();
+
+    /**
+     * Returns {@code true} if this Spliterator's {@link
+     * #characteristics} contain all of the given characteristics.
+     *
+     * @implSpec
+     * The default implementation returns true if the corresponding bits
+     * of the given characteristics are set.
+     *
+     * @param characteristics the characteristics to check for
+     * @return {@code true} if all the specified characteristics are present,
+     * else {@code false}
+     */
+    default boolean hasCharacteristics(int characteristics) {
+        return (characteristics() & characteristics) == characteristics;
+    }
+
+    /**
+     * If this Spliterator's source is {@link #SORTED} by a {@link Comparator},
+     * returns that {@code Comparator}. If the source is {@code SORTED} in
+     * {@linkplain Comparable natural order}, returns {@code null}.  Otherwise,
+     * if the source is not {@code SORTED}, throws {@link IllegalStateException}.
+     *
+     * @implSpec
+     * The default implementation always throws {@link IllegalStateException}.
+     *
+     * @return a Comparator, or {@code null} if the elements are sorted in the
+     * natural order.
+     * @throws IllegalStateException if the spliterator does not report
+     *         a characteristic of {@code SORTED}.
+     */
+    default Comparator<? super T> getComparator() {
+        throw new IllegalStateException();
+    }
+
+    /**
+     * Characteristic value signifying that an encounter order is defined for
+     * elements. If so, this Spliterator guarantees that method
+     * {@link #trySplit} splits a strict prefix of elements, that method
+     * {@link #tryAdvance} steps by one element in prefix order, and that
+     * {@link #forEachRemaining} performs actions in encounter order.
+     *
+     * <p>A {@link Collection} has an encounter order if the corresponding
+     * {@link Collection#iterator} documents an order. If so, the encounter
+     * order is the same as the documented order. Otherwise, a collection does
+     * not have an encounter order.
+     *
+     * @apiNote Encounter order is guaranteed to be ascending index order for
+     * any {@link List}. But no order is guaranteed for hash-based collections
+     * such as {@link HashSet}. Clients of a Spliterator that reports
+     * {@code ORDERED} are expected to preserve ordering constraints in
+     * non-commutative parallel computations.
+     */
+    public static final int ORDERED    = 0x00000010;
+
+    /**
+     * Characteristic value signifying that, for each pair of
+     * encountered elements {@code x, y}, {@code !x.equals(y)}. This
+     * applies for example, to a Spliterator based on a {@link Set}.
+     */
+    public static final int DISTINCT   = 0x00000001;
+
+    /**
+     * Characteristic value signifying that encounter order follows a defined
+     * sort order. If so, method {@link #getComparator()} returns the associated
+     * Comparator, or {@code null} if all elements are {@link Comparable} and
+     * are sorted by their natural ordering.
+     *
+     * <p>A Spliterator that reports {@code SORTED} must also report
+     * {@code ORDERED}.
+     *
+     * @apiNote The spliterators for {@code Collection} classes in the JDK that
+     * implement {@link NavigableSet} or {@link SortedSet} report {@code SORTED}.
+     */
+    public static final int SORTED     = 0x00000004;
+
+    /**
+     * Characteristic value signifying that the value returned from
+     * {@code estimateSize()} prior to traversal or splitting represents a
+     * finite size that, in the absence of structural source modification,
+     * represents an exact count of the number of elements that would be
+     * encountered by a complete traversal.
+     *
+     * @apiNote Most Spliterators for Collections, that cover all elements of a
+     * {@code Collection} report this characteristic. Sub-spliterators, such as
+     * those for {@link HashSet}, that cover a sub-set of elements and
+     * approximate their reported size do not.
+     */
+    public static final int SIZED      = 0x00000040;
+
+    /**
+     * Characteristic value signifying that the source guarantees that
+     * encountered elements will not be {@code null}. (This applies,
+     * for example, to most concurrent collections, queues, and maps.)
+     */
+    public static final int NONNULL    = 0x00000100;
+
+    /**
+     * Characteristic value signifying that the element source cannot be
+     * structurally modified; that is, elements cannot be added, replaced, or
+     * removed, so such changes cannot occur during traversal. A Spliterator
+     * that does not report {@code IMMUTABLE} or {@code CONCURRENT} is expected
+     * to have a documented policy (for example throwing
+     * {@link ConcurrentModificationException}) concerning structural
+     * interference detected during traversal.
+     */
+    public static final int IMMUTABLE  = 0x00000400;
+
+    /**
+     * Characteristic value signifying that the element source may be safely
+     * concurrently modified (allowing additions, replacements, and/or removals)
+     * by multiple threads without external synchronization. If so, the
+     * Spliterator is expected to have a documented policy concerning the impact
+     * of modifications during traversal.
+     *
+     * <p>A top-level Spliterator should not report both {@code CONCURRENT} and
+     * {@code SIZED}, since the finite size, if known, may change if the source
+     * is concurrently modified during traversal. Such a Spliterator is
+     * inconsistent and no guarantees can be made about any computation using
+     * that Spliterator. Sub-spliterators may report {@code SIZED} if the
+     * sub-split size is known and additions or removals to the source are not
+     * reflected when traversing.
+     *
+     * @apiNote Most concurrent collections maintain a consistency policy
+     * guaranteeing accuracy with respect to elements present at the point of
+     * Spliterator construction, but possibly not reflecting subsequent
+     * additions or removals.
+     */
+    public static final int CONCURRENT = 0x00001000;
+
+    /**
+     * Characteristic value signifying that all Spliterators resulting from
+     * {@code trySplit()} will be both {@link #SIZED} and {@link #SUBSIZED}.
+     * (This means that all child Spliterators, whether direct or indirect, will
+     * be {@code SIZED}.)
+     *
+     * <p>A Spliterator that does not report {@code SIZED} as required by
+     * {@code SUBSIZED} is inconsistent and no guarantees can be made about any
+     * computation using that Spliterator.
+     *
+     * @apiNote Some spliterators, such as the top-level spliterator for an
+     * approximately balanced binary tree, will report {@code SIZED} but not
+     * {@code SUBSIZED}, since it is common to know the size of the entire tree
+     * but not the exact sizes of subtrees.
+     */
+    public static final int SUBSIZED = 0x00004000;
+
+    /**
+     * A Spliterator specialized for primitive values.
+     *
+     * @param <T> the type of elements returned by this Spliterator.  The
+     * type must be a wrapper type for a primitive type, such as {@code Integer}
+     * for the primitive {@code int} type.
+     * @param <T_CONS> the type of primitive consumer.  The type must be a
+     * primitive specialization of {@link java.util.function.Consumer} for
+     * {@code T}, such as {@link java.util.function.IntConsumer} for
+     * {@code Integer}.
+     * @param <T_SPLITR> the type of primitive Spliterator.  The type must be
+     * a primitive specialization of Spliterator for {@code T}, such as
+     * {@link Spliterator.OfInt} for {@code Integer}.
+     *
+     * @see Spliterator.OfInt
+     * @see Spliterator.OfLong
+     * @see Spliterator.OfDouble
+     * @since 1.8
+     */
+    public interface OfPrimitive<T, T_CONS, T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>>
+            extends Spliterator<T> {
+        @Override
+        T_SPLITR trySplit();
+
+        /**
+         * If a remaining element exists, performs the given action on it,
+         * returning {@code true}; else returns {@code false}.  If this
+         * Spliterator is {@link #ORDERED} the action is performed on the
+         * next element in encounter order.  Exceptions thrown by the
+         * action are relayed to the caller.
+         *
+         * @param action The action
+         * @return {@code false} if no remaining elements existed
+         * upon entry to this method, else {@code true}.
+         * @throws NullPointerException if the specified action is null
+         */
+        @SuppressWarnings("overloads")
+        boolean tryAdvance(T_CONS action);
+
+        /**
+         * Performs the given action for each remaining element, sequentially in
+         * the current thread, until all elements have been processed or the
+         * action throws an exception.  If this Spliterator is {@link #ORDERED},
+         * actions are performed in encounter order.  Exceptions thrown by the
+         * action are relayed to the caller.
+         *
+         * @implSpec
+         * The default implementation repeatedly invokes {@link #tryAdvance}
+         * until it returns {@code false}.  It should be overridden whenever
+         * possible.
+         *
+         * @param action The action
+         * @throws NullPointerException if the specified action is null
+         */
+        @SuppressWarnings("overloads")
+        default void forEachRemaining(T_CONS action) {
+            do { } while (tryAdvance(action));
+        }
+    }
+
+    /**
+     * A Spliterator specialized for {@code int} values.
+     * @since 1.8
+     */
+    public interface OfInt extends OfPrimitive<Integer, IntConsumer, OfInt> {
+
+        @Override
+        OfInt trySplit();
+
+        @Override
+        boolean tryAdvance(IntConsumer action);
+
+        @Override
+        default void forEachRemaining(IntConsumer action) {
+            do { } while (tryAdvance(action));
+        }
+
+        /**
+         * {@inheritDoc}
+         * @implSpec
+         * If the action is an instance of {@code IntConsumer} then it is cast
+         * to {@code IntConsumer} and passed to
+         * {@link #tryAdvance(java.util.function.IntConsumer)}; otherwise
+         * the action is adapted to an instance of {@code IntConsumer}, by
+         * boxing the argument of {@code IntConsumer}, and then passed to
+         * {@link #tryAdvance(java.util.function.IntConsumer)}.
+         */
+        @Override
+        default boolean tryAdvance(Consumer<? super Integer> action) {
+            if (action instanceof IntConsumer) {
+                return tryAdvance((IntConsumer) action);
+            }
+            else {
+                if (Tripwire.ENABLED)
+                    Tripwire.trip(getClass(),
+                                  "{0} calling Spliterator.OfInt.tryAdvance((IntConsumer) action::accept)");
+                return tryAdvance((IntConsumer) action::accept);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * @implSpec
+         * If the action is an instance of {@code IntConsumer} then it is cast
+         * to {@code IntConsumer} and passed to
+         * {@link #forEachRemaining(java.util.function.IntConsumer)}; otherwise
+         * the action is adapted to an instance of {@code IntConsumer}, by
+         * boxing the argument of {@code IntConsumer}, and then passed to
+         * {@link #forEachRemaining(java.util.function.IntConsumer)}.
+         */
+        @Override
+        default void forEachRemaining(Consumer<? super Integer> action) {
+            if (action instanceof IntConsumer) {
+                forEachRemaining((IntConsumer) action);
+            }
+            else {
+                if (Tripwire.ENABLED)
+                    Tripwire.trip(getClass(),
+                                  "{0} calling Spliterator.OfInt.forEachRemaining((IntConsumer) action::accept)");
+                forEachRemaining((IntConsumer) action::accept);
+            }
+        }
+    }
+
+    /**
+     * A Spliterator specialized for {@code long} values.
+     * @since 1.8
+     */
+    public interface OfLong extends OfPrimitive<Long, LongConsumer, OfLong> {
+
+        @Override
+        OfLong trySplit();
+
+        @Override
+        boolean tryAdvance(LongConsumer action);
+
+        @Override
+        default void forEachRemaining(LongConsumer action) {
+            do { } while (tryAdvance(action));
+        }
+
+        /**
+         * {@inheritDoc}
+         * @implSpec
+         * If the action is an instance of {@code LongConsumer} then it is cast
+         * to {@code LongConsumer} and passed to
+         * {@link #tryAdvance(java.util.function.LongConsumer)}; otherwise
+         * the action is adapted to an instance of {@code LongConsumer}, by
+         * boxing the argument of {@code LongConsumer}, and then passed to
+         * {@link #tryAdvance(java.util.function.LongConsumer)}.
+         */
+        @Override
+        default boolean tryAdvance(Consumer<? super Long> action) {
+            if (action instanceof LongConsumer) {
+                return tryAdvance((LongConsumer) action);
+            }
+            else {
+                if (Tripwire.ENABLED)
+                    Tripwire.trip(getClass(),
+                                  "{0} calling Spliterator.OfLong.tryAdvance((LongConsumer) action::accept)");
+                return tryAdvance((LongConsumer) action::accept);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * @implSpec
+         * If the action is an instance of {@code LongConsumer} then it is cast
+         * to {@code LongConsumer} and passed to
+         * {@link #forEachRemaining(java.util.function.LongConsumer)}; otherwise
+         * the action is adapted to an instance of {@code LongConsumer}, by
+         * boxing the argument of {@code LongConsumer}, and then passed to
+         * {@link #forEachRemaining(java.util.function.LongConsumer)}.
+         */
+        @Override
+        default void forEachRemaining(Consumer<? super Long> action) {
+            if (action instanceof LongConsumer) {
+                forEachRemaining((LongConsumer) action);
+            }
+            else {
+                if (Tripwire.ENABLED)
+                    Tripwire.trip(getClass(),
+                                  "{0} calling Spliterator.OfLong.forEachRemaining((LongConsumer) action::accept)");
+                forEachRemaining((LongConsumer) action::accept);
+            }
+        }
+    }
+
+    /**
+     * A Spliterator specialized for {@code double} values.
+     * @since 1.8
+     */
+    public interface OfDouble extends OfPrimitive<Double, DoubleConsumer, OfDouble> {
+
+        @Override
+        OfDouble trySplit();
+
+        @Override
+        boolean tryAdvance(DoubleConsumer action);
+
+        @Override
+        default void forEachRemaining(DoubleConsumer action) {
+            do { } while (tryAdvance(action));
+        }
+
+        /**
+         * {@inheritDoc}
+         * @implSpec
+         * If the action is an instance of {@code DoubleConsumer} then it is
+         * cast to {@code DoubleConsumer} and passed to
+         * {@link #tryAdvance(java.util.function.DoubleConsumer)}; otherwise
+         * the action is adapted to an instance of {@code DoubleConsumer}, by
+         * boxing the argument of {@code DoubleConsumer}, and then passed to
+         * {@link #tryAdvance(java.util.function.DoubleConsumer)}.
+         */
+        @Override
+        default boolean tryAdvance(Consumer<? super Double> action) {
+            if (action instanceof DoubleConsumer) {
+                return tryAdvance((DoubleConsumer) action);
+            }
+            else {
+                if (Tripwire.ENABLED)
+                    Tripwire.trip(getClass(),
+                                  "{0} calling Spliterator.OfDouble.tryAdvance((DoubleConsumer) action::accept)");
+                return tryAdvance((DoubleConsumer) action::accept);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * @implSpec
+         * If the action is an instance of {@code DoubleConsumer} then it is
+         * cast to {@code DoubleConsumer} and passed to
+         * {@link #forEachRemaining(java.util.function.DoubleConsumer)};
+         * otherwise the action is adapted to an instance of
+         * {@code DoubleConsumer}, by boxing the argument of
+         * {@code DoubleConsumer}, and then passed to
+         * {@link #forEachRemaining(java.util.function.DoubleConsumer)}.
+         */
+        @Override
+        default void forEachRemaining(Consumer<? super Double> action) {
+            if (action instanceof DoubleConsumer) {
+                forEachRemaining((DoubleConsumer) action);
+            }
+            else {
+                if (Tripwire.ENABLED)
+                    Tripwire.trip(getClass(),
+                                  "{0} calling Spliterator.OfDouble.forEachRemaining((DoubleConsumer) action::accept)");
+                forEachRemaining((DoubleConsumer) action::accept);
+            }
+        }
+    }
+}
diff --git a/ojluni/src/main/java/java/util/Spliterators.java b/ojluni/src/main/java/java/util/Spliterators.java
new file mode 100644
index 0000000..3f97a83
--- /dev/null
+++ b/ojluni/src/main/java/java/util/Spliterators.java
@@ -0,0 +1,2124 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+/**
+ * Static classes and methods for operating on or creating instances of
+ * {@link Spliterator} and its primitive specializations
+ * {@link Spliterator.OfInt}, {@link Spliterator.OfLong}, and
+ * {@link Spliterator.OfDouble}.
+ *
+ * @see Spliterator
+ * @since 1.8
+ */
+public final class Spliterators {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private Spliterators() {}
+
+    // Empty spliterators
+
+    /**
+     * Creates an empty {@code Spliterator}
+     *
+     * <p>The empty spliterator reports {@link Spliterator#SIZED} and
+     * {@link Spliterator#SUBSIZED}.  Calls to
+     * {@link java.util.Spliterator#trySplit()} always return {@code null}.
+     *
+     * @param <T> Type of elements
+     * @return An empty spliterator
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> Spliterator<T> emptySpliterator() {
+        return (Spliterator<T>) EMPTY_SPLITERATOR;
+    }
+
+    private static final Spliterator<Object> EMPTY_SPLITERATOR =
+            new EmptySpliterator.OfRef<>();
+
+    /**
+     * Creates an empty {@code Spliterator.OfInt}
+     *
+     * <p>The empty spliterator reports {@link Spliterator#SIZED} and
+     * {@link Spliterator#SUBSIZED}.  Calls to
+     * {@link java.util.Spliterator#trySplit()} always return {@code null}.
+     *
+     * @return An empty spliterator
+     */
+    public static Spliterator.OfInt emptyIntSpliterator() {
+        return EMPTY_INT_SPLITERATOR;
+    }
+
+    private static final Spliterator.OfInt EMPTY_INT_SPLITERATOR =
+            new EmptySpliterator.OfInt();
+
+    /**
+     * Creates an empty {@code Spliterator.OfLong}
+     *
+     * <p>The empty spliterator reports {@link Spliterator#SIZED} and
+     * {@link Spliterator#SUBSIZED}.  Calls to
+     * {@link java.util.Spliterator#trySplit()} always return {@code null}.
+     *
+     * @return An empty spliterator
+     */
+    public static Spliterator.OfLong emptyLongSpliterator() {
+        return EMPTY_LONG_SPLITERATOR;
+    }
+
+    private static final Spliterator.OfLong EMPTY_LONG_SPLITERATOR =
+            new EmptySpliterator.OfLong();
+
+    /**
+     * Creates an empty {@code Spliterator.OfDouble}
+     *
+     * <p>The empty spliterator reports {@link Spliterator#SIZED} and
+     * {@link Spliterator#SUBSIZED}.  Calls to
+     * {@link java.util.Spliterator#trySplit()} always return {@code null}.
+     *
+     * @return An empty spliterator
+     */
+    public static Spliterator.OfDouble emptyDoubleSpliterator() {
+        return EMPTY_DOUBLE_SPLITERATOR;
+    }
+
+    private static final Spliterator.OfDouble EMPTY_DOUBLE_SPLITERATOR =
+            new EmptySpliterator.OfDouble();
+
+    // Array-based spliterators
+
+    /**
+     * Creates a {@code Spliterator} covering the elements of a given array,
+     * using a customized set of spliterator characteristics.
+     *
+     * <p>This method is provided as an implementation convenience for
+     * Spliterators which store portions of their elements in arrays, and need
+     * fine control over Spliterator characteristics.  Most other situations in
+     * which a Spliterator for an array is needed should use
+     * {@link Arrays#spliterator(Object[])}.
+     *
+     * <p>The returned spliterator always reports the characteristics
+     * {@code SIZED} and {@code SUBSIZED}.  The caller may provide additional
+     * characteristics for the spliterator to report; it is common to
+     * additionally specify {@code IMMUTABLE} and {@code ORDERED}.
+     *
+     * @param <T> Type of elements
+     * @param array The array, assumed to be unmodified during use
+     * @param additionalCharacteristics Additional spliterator characteristics
+     *        of this spliterator's source or elements beyond {@code SIZED} and
+     *        {@code SUBSIZED} which are are always reported
+     * @return A spliterator for an array
+     * @throws NullPointerException if the given array is {@code null}
+     * @see Arrays#spliterator(Object[])
+     */
+    public static <T> Spliterator<T> spliterator(Object[] array,
+                                                 int additionalCharacteristics) {
+        return new ArraySpliterator<>(Objects.requireNonNull(array),
+                                      additionalCharacteristics);
+    }
+
+    /**
+     * Creates a {@code Spliterator} covering a range of elements of a given
+     * array, using a customized set of spliterator characteristics.
+     *
+     * <p>This method is provided as an implementation convenience for
+     * Spliterators which store portions of their elements in arrays, and need
+     * fine control over Spliterator characteristics.  Most other situations in
+     * which a Spliterator for an array is needed should use
+     * {@link Arrays#spliterator(Object[])}.
+     *
+     * <p>The returned spliterator always reports the characteristics
+     * {@code SIZED} and {@code SUBSIZED}.  The caller may provide additional
+     * characteristics for the spliterator to report; it is common to
+     * additionally specify {@code IMMUTABLE} and {@code ORDERED}.
+     *
+     * @param <T> Type of elements
+     * @param array The array, assumed to be unmodified during use
+     * @param fromIndex The least index (inclusive) to cover
+     * @param toIndex One past the greatest index to cover
+     * @param additionalCharacteristics Additional spliterator characteristics
+     *        of this spliterator's source or elements beyond {@code SIZED} and
+     *        {@code SUBSIZED} which are are always reported
+     * @return A spliterator for an array
+     * @throws NullPointerException if the given array is {@code null}
+     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex} is negative,
+     *         {@code toIndex} is less than {@code fromIndex}, or
+     *         {@code toIndex} is greater than the array size
+     * @see Arrays#spliterator(Object[], int, int)
+     */
+    public static <T> Spliterator<T> spliterator(Object[] array, int fromIndex, int toIndex,
+                                                 int additionalCharacteristics) {
+        checkFromToBounds(Objects.requireNonNull(array).length, fromIndex, toIndex);
+        return new ArraySpliterator<>(array, fromIndex, toIndex, additionalCharacteristics);
+    }
+
+    /**
+     * Creates a {@code Spliterator.OfInt} covering the elements of a given array,
+     * using a customized set of spliterator characteristics.
+     *
+     * <p>This method is provided as an implementation convenience for
+     * Spliterators which store portions of their elements in arrays, and need
+     * fine control over Spliterator characteristics.  Most other situations in
+     * which a Spliterator for an array is needed should use
+     * {@link Arrays#spliterator(int[])}.
+     *
+     * <p>The returned spliterator always reports the characteristics
+     * {@code SIZED} and {@code SUBSIZED}.  The caller may provide additional
+     * characteristics for the spliterator to report; it is common to
+     * additionally specify {@code IMMUTABLE} and {@code ORDERED}.
+     *
+     * @param array The array, assumed to be unmodified during use
+     * @param additionalCharacteristics Additional spliterator characteristics
+     *        of this spliterator's source or elements beyond {@code SIZED} and
+     *        {@code SUBSIZED} which are are always reported
+     * @return A spliterator for an array
+     * @throws NullPointerException if the given array is {@code null}
+     * @see Arrays#spliterator(int[])
+     */
+    public static Spliterator.OfInt spliterator(int[] array,
+                                                int additionalCharacteristics) {
+        return new IntArraySpliterator(Objects.requireNonNull(array), additionalCharacteristics);
+    }
+
+    /**
+     * Creates a {@code Spliterator.OfInt} covering a range of elements of a
+     * given array, using a customized set of spliterator characteristics.
+     *
+     * <p>This method is provided as an implementation convenience for
+     * Spliterators which store portions of their elements in arrays, and need
+     * fine control over Spliterator characteristics.  Most other situations in
+     * which a Spliterator for an array is needed should use
+     * {@link Arrays#spliterator(int[], int, int)}.
+     *
+     * <p>The returned spliterator always reports the characteristics
+     * {@code SIZED} and {@code SUBSIZED}.  The caller may provide additional
+     * characteristics for the spliterator to report; it is common to
+     * additionally specify {@code IMMUTABLE} and {@code ORDERED}.
+     *
+     * @param array The array, assumed to be unmodified during use
+     * @param fromIndex The least index (inclusive) to cover
+     * @param toIndex One past the greatest index to cover
+     * @param additionalCharacteristics Additional spliterator characteristics
+     *        of this spliterator's source or elements beyond {@code SIZED} and
+     *        {@code SUBSIZED} which are are always reported
+     * @return A spliterator for an array
+     * @throws NullPointerException if the given array is {@code null}
+     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex} is negative,
+     *         {@code toIndex} is less than {@code fromIndex}, or
+     *         {@code toIndex} is greater than the array size
+     * @see Arrays#spliterator(int[], int, int)
+     */
+    public static Spliterator.OfInt spliterator(int[] array, int fromIndex, int toIndex,
+                                                int additionalCharacteristics) {
+        checkFromToBounds(Objects.requireNonNull(array).length, fromIndex, toIndex);
+        return new IntArraySpliterator(array, fromIndex, toIndex, additionalCharacteristics);
+    }
+
+    /**
+     * Creates a {@code Spliterator.OfLong} covering the elements of a given array,
+     * using a customized set of spliterator characteristics.
+     *
+     * <p>This method is provided as an implementation convenience for
+     * Spliterators which store portions of their elements in arrays, and need
+     * fine control over Spliterator characteristics.  Most other situations in
+     * which a Spliterator for an array is needed should use
+     * {@link Arrays#spliterator(long[])}.
+     *
+     * <p>The returned spliterator always reports the characteristics
+     * {@code SIZED} and {@code SUBSIZED}.  The caller may provide additional
+     * characteristics for the spliterator to report; it is common to
+     * additionally specify {@code IMMUTABLE} and {@code ORDERED}.
+     *
+     * @param array The array, assumed to be unmodified during use
+     * @param additionalCharacteristics Additional spliterator characteristics
+     *        of this spliterator's source or elements beyond {@code SIZED} and
+     *        {@code SUBSIZED} which are are always reported
+     * @return A spliterator for an array
+     * @throws NullPointerException if the given array is {@code null}
+     * @see Arrays#spliterator(long[])
+     */
+    public static Spliterator.OfLong spliterator(long[] array,
+                                                 int additionalCharacteristics) {
+        return new LongArraySpliterator(Objects.requireNonNull(array), additionalCharacteristics);
+    }
+
+    /**
+     * Creates a {@code Spliterator.OfLong} covering a range of elements of a
+     * given array, using a customized set of spliterator characteristics.
+     *
+     * <p>This method is provided as an implementation convenience for
+     * Spliterators which store portions of their elements in arrays, and need
+     * fine control over Spliterator characteristics.  Most other situations in
+     * which a Spliterator for an array is needed should use
+     * {@link Arrays#spliterator(long[], int, int)}.
+     *
+     * <p>The returned spliterator always reports the characteristics
+     * {@code SIZED} and {@code SUBSIZED}.  The caller may provide additional
+     * characteristics for the spliterator to report.  (For example, if it is
+     * known the array will not be further modified, specify {@code IMMUTABLE};
+     * if the array data is considered to have an an encounter order, specify
+     * {@code ORDERED}).  The method {@link Arrays#spliterator(long[], int, int)} can
+     * often be used instead, which returns a spliterator that reports
+     * {@code SIZED}, {@code SUBSIZED}, {@code IMMUTABLE}, and {@code ORDERED}.
+     *
+     * @param array The array, assumed to be unmodified during use
+     * @param fromIndex The least index (inclusive) to cover
+     * @param toIndex One past the greatest index to cover
+     * @param additionalCharacteristics Additional spliterator characteristics
+     *        of this spliterator's source or elements beyond {@code SIZED} and
+     *        {@code SUBSIZED} which are are always reported
+     * @return A spliterator for an array
+     * @throws NullPointerException if the given array is {@code null}
+     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex} is negative,
+     *         {@code toIndex} is less than {@code fromIndex}, or
+     *         {@code toIndex} is greater than the array size
+     * @see Arrays#spliterator(long[], int, int)
+     */
+    public static Spliterator.OfLong spliterator(long[] array, int fromIndex, int toIndex,
+                                                 int additionalCharacteristics) {
+        checkFromToBounds(Objects.requireNonNull(array).length, fromIndex, toIndex);
+        return new LongArraySpliterator(array, fromIndex, toIndex, additionalCharacteristics);
+    }
+
+    /**
+     * Creates a {@code Spliterator.OfDouble} covering the elements of a given array,
+     * using a customized set of spliterator characteristics.
+     *
+     * <p>This method is provided as an implementation convenience for
+     * Spliterators which store portions of their elements in arrays, and need
+     * fine control over Spliterator characteristics.  Most other situations in
+     * which a Spliterator for an array is needed should use
+     * {@link Arrays#spliterator(double[])}.
+     *
+     * <p>The returned spliterator always reports the characteristics
+     * {@code SIZED} and {@code SUBSIZED}.  The caller may provide additional
+     * characteristics for the spliterator to report; it is common to
+     * additionally specify {@code IMMUTABLE} and {@code ORDERED}.
+     *
+     * @param array The array, assumed to be unmodified during use
+     * @param additionalCharacteristics Additional spliterator characteristics
+     *        of this spliterator's source or elements beyond {@code SIZED} and
+     *        {@code SUBSIZED} which are are always reported
+     * @return A spliterator for an array
+     * @throws NullPointerException if the given array is {@code null}
+     * @see Arrays#spliterator(double[])
+     */
+    public static Spliterator.OfDouble spliterator(double[] array,
+                                                   int additionalCharacteristics) {
+        return new DoubleArraySpliterator(Objects.requireNonNull(array), additionalCharacteristics);
+    }
+
+    /**
+     * Creates a {@code Spliterator.OfDouble} covering a range of elements of a
+     * given array, using a customized set of spliterator characteristics.
+     *
+     * <p>This method is provided as an implementation convenience for
+     * Spliterators which store portions of their elements in arrays, and need
+     * fine control over Spliterator characteristics.  Most other situations in
+     * which a Spliterator for an array is needed should use
+     * {@link Arrays#spliterator(double[], int, int)}.
+     *
+     * <p>The returned spliterator always reports the characteristics
+     * {@code SIZED} and {@code SUBSIZED}.  The caller may provide additional
+     * characteristics for the spliterator to report.  (For example, if it is
+     * known the array will not be further modified, specify {@code IMMUTABLE};
+     * if the array data is considered to have an an encounter order, specify
+     * {@code ORDERED}).  The method {@link Arrays#spliterator(long[], int, int)} can
+     * often be used instead, which returns a spliterator that reports
+     * {@code SIZED}, {@code SUBSIZED}, {@code IMMUTABLE}, and {@code ORDERED}.
+     *
+     * @param array The array, assumed to be unmodified during use
+     * @param fromIndex The least index (inclusive) to cover
+     * @param toIndex One past the greatest index to cover
+     * @param additionalCharacteristics Additional spliterator characteristics
+     *        of this spliterator's source or elements beyond {@code SIZED} and
+     *        {@code SUBSIZED} which are are always reported
+     * @return A spliterator for an array
+     * @throws NullPointerException if the given array is {@code null}
+     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex} is negative,
+     *         {@code toIndex} is less than {@code fromIndex}, or
+     *         {@code toIndex} is greater than the array size
+     * @see Arrays#spliterator(double[], int, int)
+     */
+    public static Spliterator.OfDouble spliterator(double[] array, int fromIndex, int toIndex,
+                                                   int additionalCharacteristics) {
+        checkFromToBounds(Objects.requireNonNull(array).length, fromIndex, toIndex);
+        return new DoubleArraySpliterator(array, fromIndex, toIndex, additionalCharacteristics);
+    }
+
+    /**
+     * Validate inclusive start index and exclusive end index against the length
+     * of an array.
+     * @param arrayLength The length of the array
+     * @param origin The inclusive start index
+     * @param fence The exclusive end index
+     * @throws ArrayIndexOutOfBoundsException if the start index is greater than
+     * the end index, if the start index is negative, or the end index is
+     * greater than the array length
+     */
+    private static void checkFromToBounds(int arrayLength, int origin, int fence) {
+        if (origin > fence) {
+            throw new IllegalArgumentException(
+                    "origin(" + origin + ") > fence(" + fence + ")");
+        }
+        if (origin < 0) {
+            throw new ArrayIndexOutOfBoundsException(origin);
+        }
+        if (fence > arrayLength) {
+            throw new ArrayIndexOutOfBoundsException(fence);
+        }
+    }
+
+    // Iterator-based spliterators
+
+    /**
+     * Creates a {@code Spliterator} using the given collection's
+     * {@link java.util.Collection#iterator()} as the source of elements, and
+     * reporting its {@link java.util.Collection#size()} as its initial size.
+     *
+     * <p>The spliterator is
+     * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
+     * the <em>fail-fast</em> properties of the collection's iterator, and
+     * implements {@code trySplit} to permit limited parallelism.
+     *
+     * @param <T> Type of elements
+     * @param c The collection
+     * @param characteristics Characteristics of this spliterator's source or
+     *        elements.  The characteristics {@code SIZED} and {@code SUBSIZED}
+     *        are additionally reported unless {@code CONCURRENT} is supplied.
+     * @return A spliterator from an iterator
+     * @throws NullPointerException if the given collection is {@code null}
+     */
+    public static <T> Spliterator<T> spliterator(Collection<? extends T> c,
+                                                 int characteristics) {
+        return new IteratorSpliterator<>(Objects.requireNonNull(c),
+                                         characteristics);
+    }
+
+    /**
+     * Creates a {@code Spliterator} using a given {@code Iterator}
+     * as the source of elements, and with a given initially reported size.
+     *
+     * <p>The spliterator is not
+     * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
+     * the <em>fail-fast</em> properties of the iterator, and implements
+     * {@code trySplit} to permit limited parallelism.
+     *
+     * <p>Traversal of elements should be accomplished through the spliterator.
+     * The behaviour of splitting and traversal is undefined if the iterator is
+     * operated on after the spliterator is returned, or the initially reported
+     * size is not equal to the actual number of elements in the source.
+     *
+     * @param <T> Type of elements
+     * @param iterator The iterator for the source
+     * @param size The number of elements in the source, to be reported as
+     *        initial {@code estimateSize}
+     * @param characteristics Characteristics of this spliterator's source or
+     *        elements.  The characteristics {@code SIZED} and {@code SUBSIZED}
+     *        are additionally reported unless {@code CONCURRENT} is supplied.
+     * @return A spliterator from an iterator
+     * @throws NullPointerException if the given iterator is {@code null}
+     */
+    public static <T> Spliterator<T> spliterator(Iterator<? extends T> iterator,
+                                                 long size,
+                                                 int characteristics) {
+        return new IteratorSpliterator<>(Objects.requireNonNull(iterator), size,
+                                         characteristics);
+    }
+
+    /**
+     * Creates a {@code Spliterator} using a given {@code Iterator}
+     * as the source of elements, with no initial size estimate.
+     *
+     * <p>The spliterator is not
+     * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
+     * the <em>fail-fast</em> properties of the iterator, and implements
+     * {@code trySplit} to permit limited parallelism.
+     *
+     * <p>Traversal of elements should be accomplished through the spliterator.
+     * The behaviour of splitting and traversal is undefined if the iterator is
+     * operated on after the spliterator is returned.
+     *
+     * @param <T> Type of elements
+     * @param iterator The iterator for the source
+     * @param characteristics Characteristics of this spliterator's source
+     *        or elements ({@code SIZED} and {@code SUBSIZED}, if supplied, are
+     *        ignored and are not reported.)
+     * @return A spliterator from an iterator
+     * @throws NullPointerException if the given iterator is {@code null}
+     */
+    public static <T> Spliterator<T> spliteratorUnknownSize(Iterator<? extends T> iterator,
+                                                            int characteristics) {
+        return new IteratorSpliterator<>(Objects.requireNonNull(iterator), characteristics);
+    }
+
+    /**
+     * Creates a {@code Spliterator.OfInt} using a given
+     * {@code IntStream.IntIterator} as the source of elements, and with a given
+     * initially reported size.
+     *
+     * <p>The spliterator is not
+     * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
+     * the <em>fail-fast</em> properties of the iterator, and implements
+     * {@code trySplit} to permit limited parallelism.
+     *
+     * <p>Traversal of elements should be accomplished through the spliterator.
+     * The behaviour of splitting and traversal is undefined if the iterator is
+     * operated on after the spliterator is returned, or the initially reported
+     * size is not equal to the actual number of elements in the source.
+     *
+     * @param iterator The iterator for the source
+     * @param size The number of elements in the source, to be reported as
+     *        initial {@code estimateSize}.
+     * @param characteristics Characteristics of this spliterator's source or
+     *        elements.  The characteristics {@code SIZED} and {@code SUBSIZED}
+     *        are additionally reported unless {@code CONCURRENT} is supplied.
+     * @return A spliterator from an iterator
+     * @throws NullPointerException if the given iterator is {@code null}
+     */
+    public static Spliterator.OfInt spliterator(PrimitiveIterator.OfInt iterator,
+                                                long size,
+                                                int characteristics) {
+        return new IntIteratorSpliterator(Objects.requireNonNull(iterator),
+                                          size, characteristics);
+    }
+
+    /**
+     * Creates a {@code Spliterator.OfInt} using a given
+     * {@code IntStream.IntIterator} as the source of elements, with no initial
+     * size estimate.
+     *
+     * <p>The spliterator is not
+     * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
+     * the <em>fail-fast</em> properties of the iterator, and implements
+     * {@code trySplit} to permit limited parallelism.
+     *
+     * <p>Traversal of elements should be accomplished through the spliterator.
+     * The behaviour of splitting and traversal is undefined if the iterator is
+     * operated on after the spliterator is returned.
+     *
+     * @param iterator The iterator for the source
+     * @param characteristics Characteristics of this spliterator's source
+     *        or elements ({@code SIZED} and {@code SUBSIZED}, if supplied, are
+     *        ignored and are not reported.)
+     * @return A spliterator from an iterator
+     * @throws NullPointerException if the given iterator is {@code null}
+     */
+    public static Spliterator.OfInt spliteratorUnknownSize(PrimitiveIterator.OfInt iterator,
+                                                           int characteristics) {
+        return new IntIteratorSpliterator(Objects.requireNonNull(iterator), characteristics);
+    }
+
+    /**
+     * Creates a {@code Spliterator.OfLong} using a given
+     * {@code LongStream.LongIterator} as the source of elements, and with a
+     * given initially reported size.
+     *
+     * <p>The spliterator is not
+     * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
+     * the <em>fail-fast</em> properties of the iterator, and implements
+     * {@code trySplit} to permit limited parallelism.
+     *
+     * <p>Traversal of elements should be accomplished through the spliterator.
+     * The behaviour of splitting and traversal is undefined if the iterator is
+     * operated on after the spliterator is returned, or the initially reported
+     * size is not equal to the actual number of elements in the source.
+     *
+     * @param iterator The iterator for the source
+     * @param size The number of elements in the source, to be reported as
+     *        initial {@code estimateSize}.
+     * @param characteristics Characteristics of this spliterator's source or
+     *        elements.  The characteristics {@code SIZED} and {@code SUBSIZED}
+     *        are additionally reported unless {@code CONCURRENT} is supplied.
+     * @return A spliterator from an iterator
+     * @throws NullPointerException if the given iterator is {@code null}
+     */
+    public static Spliterator.OfLong spliterator(PrimitiveIterator.OfLong iterator,
+                                                 long size,
+                                                 int characteristics) {
+        return new LongIteratorSpliterator(Objects.requireNonNull(iterator),
+                                           size, characteristics);
+    }
+
+    /**
+     * Creates a {@code Spliterator.OfLong} using a given
+     * {@code LongStream.LongIterator} as the source of elements, with no
+     * initial size estimate.
+     *
+     * <p>The spliterator is not
+     * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
+     * the <em>fail-fast</em> properties of the iterator, and implements
+     * {@code trySplit} to permit limited parallelism.
+     *
+     * <p>Traversal of elements should be accomplished through the spliterator.
+     * The behaviour of splitting and traversal is undefined if the iterator is
+     * operated on after the spliterator is returned.
+     *
+     * @param iterator The iterator for the source
+     * @param characteristics Characteristics of this spliterator's source
+     *        or elements ({@code SIZED} and {@code SUBSIZED}, if supplied, are
+     *        ignored and are not reported.)
+     * @return A spliterator from an iterator
+     * @throws NullPointerException if the given iterator is {@code null}
+     */
+    public static Spliterator.OfLong spliteratorUnknownSize(PrimitiveIterator.OfLong iterator,
+                                                            int characteristics) {
+        return new LongIteratorSpliterator(Objects.requireNonNull(iterator), characteristics);
+    }
+
+    /**
+     * Creates a {@code Spliterator.OfDouble} using a given
+     * {@code DoubleStream.DoubleIterator} as the source of elements, and with a
+     * given initially reported size.
+     *
+     * <p>The spliterator is not
+     * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
+     * the <em>fail-fast</em> properties of the iterator, and implements
+     * {@code trySplit} to permit limited parallelism.
+     *
+     * <p>Traversal of elements should be accomplished through the spliterator.
+     * The behaviour of splitting and traversal is undefined if the iterator is
+     * operated on after the spliterator is returned, or the initially reported
+     * size is not equal to the actual number of elements in the source.
+     *
+     * @param iterator The iterator for the source
+     * @param size The number of elements in the source, to be reported as
+     *        initial {@code estimateSize}
+     * @param characteristics Characteristics of this spliterator's source or
+     *        elements.  The characteristics {@code SIZED} and {@code SUBSIZED}
+     *        are additionally reported unless {@code CONCURRENT} is supplied.
+     * @return A spliterator from an iterator
+     * @throws NullPointerException if the given iterator is {@code null}
+     */
+    public static Spliterator.OfDouble spliterator(PrimitiveIterator.OfDouble iterator,
+                                                   long size,
+                                                   int characteristics) {
+        return new DoubleIteratorSpliterator(Objects.requireNonNull(iterator),
+                                             size, characteristics);
+    }
+
+    /**
+     * Creates a {@code Spliterator.OfDouble} using a given
+     * {@code DoubleStream.DoubleIterator} as the source of elements, with no
+     * initial size estimate.
+     *
+     * <p>The spliterator is not
+     * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
+     * the <em>fail-fast</em> properties of the iterator, and implements
+     * {@code trySplit} to permit limited parallelism.
+     *
+     * <p>Traversal of elements should be accomplished through the spliterator.
+     * The behaviour of splitting and traversal is undefined if the iterator is
+     * operated on after the spliterator is returned.
+     *
+     * @param iterator The iterator for the source
+     * @param characteristics Characteristics of this spliterator's source
+     *        or elements ({@code SIZED} and {@code SUBSIZED}, if supplied, are
+     *        ignored and are not reported.)
+     * @return A spliterator from an iterator
+     * @throws NullPointerException if the given iterator is {@code null}
+     */
+    public static Spliterator.OfDouble spliteratorUnknownSize(PrimitiveIterator.OfDouble iterator,
+                                                              int characteristics) {
+        return new DoubleIteratorSpliterator(Objects.requireNonNull(iterator), characteristics);
+    }
+
+    // Iterators from Spliterators
+
+    /**
+     * Creates an {@code Iterator} from a {@code Spliterator}.
+     *
+     * <p>Traversal of elements should be accomplished through the iterator.
+     * The behaviour of traversal is undefined if the spliterator is operated
+     * after the iterator is returned.
+     *
+     * @param <T> Type of elements
+     * @param spliterator The spliterator
+     * @return An iterator
+     * @throws NullPointerException if the given spliterator is {@code null}
+     */
+    public static<T> Iterator<T> iterator(Spliterator<? extends T> spliterator) {
+        Objects.requireNonNull(spliterator);
+        class Adapter implements Iterator<T>, Consumer<T> {
+            boolean valueReady = false;
+            T nextElement;
+
+            @Override
+            public void accept(T t) {
+                valueReady = true;
+                nextElement = t;
+            }
+
+            @Override
+            public boolean hasNext() {
+                if (!valueReady)
+                    spliterator.tryAdvance(this);
+                return valueReady;
+            }
+
+            @Override
+            public T next() {
+                if (!valueReady && !hasNext())
+                    throw new NoSuchElementException();
+                else {
+                    valueReady = false;
+                    return nextElement;
+                }
+            }
+        }
+
+        return new Adapter();
+    }
+
+    /**
+     * Creates an {@code PrimitiveIterator.OfInt} from a
+     * {@code Spliterator.OfInt}.
+     *
+     * <p>Traversal of elements should be accomplished through the iterator.
+     * The behaviour of traversal is undefined if the spliterator is operated
+     * after the iterator is returned.
+     *
+     * @param spliterator The spliterator
+     * @return An iterator
+     * @throws NullPointerException if the given spliterator is {@code null}
+     */
+    public static PrimitiveIterator.OfInt iterator(Spliterator.OfInt spliterator) {
+        Objects.requireNonNull(spliterator);
+        class Adapter implements PrimitiveIterator.OfInt, IntConsumer {
+            boolean valueReady = false;
+            int nextElement;
+
+            @Override
+            public void accept(int t) {
+                valueReady = true;
+                nextElement = t;
+            }
+
+            @Override
+            public boolean hasNext() {
+                if (!valueReady)
+                    spliterator.tryAdvance(this);
+                return valueReady;
+            }
+
+            @Override
+            public int nextInt() {
+                if (!valueReady && !hasNext())
+                    throw new NoSuchElementException();
+                else {
+                    valueReady = false;
+                    return nextElement;
+                }
+            }
+        }
+
+        return new Adapter();
+    }
+
+    /**
+     * Creates an {@code PrimitiveIterator.OfLong} from a
+     * {@code Spliterator.OfLong}.
+     *
+     * <p>Traversal of elements should be accomplished through the iterator.
+     * The behaviour of traversal is undefined if the spliterator is operated
+     * after the iterator is returned.
+     *
+     * @param spliterator The spliterator
+     * @return An iterator
+     * @throws NullPointerException if the given spliterator is {@code null}
+     */
+    public static PrimitiveIterator.OfLong iterator(Spliterator.OfLong spliterator) {
+        Objects.requireNonNull(spliterator);
+        class Adapter implements PrimitiveIterator.OfLong, LongConsumer {
+            boolean valueReady = false;
+            long nextElement;
+
+            @Override
+            public void accept(long t) {
+                valueReady = true;
+                nextElement = t;
+            }
+
+            @Override
+            public boolean hasNext() {
+                if (!valueReady)
+                    spliterator.tryAdvance(this);
+                return valueReady;
+            }
+
+            @Override
+            public long nextLong() {
+                if (!valueReady && !hasNext())
+                    throw new NoSuchElementException();
+                else {
+                    valueReady = false;
+                    return nextElement;
+                }
+            }
+        }
+
+        return new Adapter();
+    }
+
+    /**
+     * Creates an {@code PrimitiveIterator.OfDouble} from a
+     * {@code Spliterator.OfDouble}.
+     *
+     * <p>Traversal of elements should be accomplished through the iterator.
+     * The behaviour of traversal is undefined if the spliterator is operated
+     * after the iterator is returned.
+     *
+     * @param spliterator The spliterator
+     * @return An iterator
+     * @throws NullPointerException if the given spliterator is {@code null}
+     */
+    public static PrimitiveIterator.OfDouble iterator(Spliterator.OfDouble spliterator) {
+        Objects.requireNonNull(spliterator);
+        class Adapter implements PrimitiveIterator.OfDouble, DoubleConsumer {
+            boolean valueReady = false;
+            double nextElement;
+
+            @Override
+            public void accept(double t) {
+                valueReady = true;
+                nextElement = t;
+            }
+
+            @Override
+            public boolean hasNext() {
+                if (!valueReady)
+                    spliterator.tryAdvance(this);
+                return valueReady;
+            }
+
+            @Override
+            public double nextDouble() {
+                if (!valueReady && !hasNext())
+                    throw new NoSuchElementException();
+                else {
+                    valueReady = false;
+                    return nextElement;
+                }
+            }
+        }
+
+        return new Adapter();
+    }
+
+    // Implementations
+
+    private static abstract class EmptySpliterator<T, S extends Spliterator<T>, C> {
+
+        EmptySpliterator() { }
+
+        public S trySplit() {
+            return null;
+        }
+
+        public boolean tryAdvance(C consumer) {
+            Objects.requireNonNull(consumer);
+            return false;
+        }
+
+        public void forEachRemaining(C consumer) {
+            Objects.requireNonNull(consumer);
+        }
+
+        public long estimateSize() {
+            return 0;
+        }
+
+        public int characteristics() {
+            return Spliterator.SIZED | Spliterator.SUBSIZED;
+        }
+
+        private static final class OfRef<T>
+                extends EmptySpliterator<T, Spliterator<T>, Consumer<? super T>>
+                implements Spliterator<T> {
+            OfRef() { }
+        }
+
+        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 OfDouble
+                extends EmptySpliterator<Double, Spliterator.OfDouble, DoubleConsumer>
+                implements Spliterator.OfDouble {
+            OfDouble() { }
+        }
+    }
+
+    // Array-based spliterators
+
+    /**
+     * A Spliterator designed for use by sources that traverse and split
+     * elements maintained in an unmodifiable {@code Object[]} array.
+     */
+    static final class ArraySpliterator<T> implements Spliterator<T> {
+        /**
+         * The array, explicitly typed as Object[]. Unlike in some other
+         * classes (see for example CR 6260652), we do not need to
+         * screen arguments to ensure they are exactly of type Object[]
+         * so long as no methods write into the array or serialize it,
+         * which we ensure here by defining this class as final.
+         */
+        private final Object[] array;
+        private int index;        // current index, modified on advance/split
+        private final int fence;  // one past last index
+        private final int characteristics;
+
+        /**
+         * Creates a spliterator covering all of the given array.
+         * @param array the array, assumed to be unmodified during use
+         * @param additionalCharacteristics Additional spliterator characteristics
+         * of this spliterator's source or elements beyond {@code SIZED} and
+         * {@code SUBSIZED} which are are always reported
+         */
+        public ArraySpliterator(Object[] array, int additionalCharacteristics) {
+            this(array, 0, array.length, additionalCharacteristics);
+        }
+
+        /**
+         * Creates a spliterator covering the given array and range
+         * @param array the array, assumed to be unmodified during use
+         * @param origin the least index (inclusive) to cover
+         * @param fence one past the greatest index to cover
+         * @param additionalCharacteristics Additional spliterator characteristics
+         * of this spliterator's source or elements beyond {@code SIZED} and
+         * {@code SUBSIZED} which are are always reported
+         */
+        public ArraySpliterator(Object[] array, int origin, int fence, int additionalCharacteristics) {
+            this.array = array;
+            this.index = origin;
+            this.fence = fence;
+            this.characteristics = additionalCharacteristics | Spliterator.SIZED | Spliterator.SUBSIZED;
+        }
+
+        @Override
+        public Spliterator<T> trySplit() {
+            int lo = index, mid = (lo + fence) >>> 1;
+            return (lo >= mid)
+                   ? null
+                   : new ArraySpliterator<>(array, lo, index = mid, characteristics);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void forEachRemaining(Consumer<? super T> action) {
+            Object[] a; int i, hi; // hoist accesses and checks from loop
+            if (action == null)
+                throw new NullPointerException();
+            if ((a = array).length >= (hi = fence) &&
+                (i = index) >= 0 && i < (index = hi)) {
+                do { action.accept((T)a[i]); } while (++i < hi);
+            }
+        }
+
+        @Override
+        public boolean tryAdvance(Consumer<? super T> action) {
+            if (action == null)
+                throw new NullPointerException();
+            if (index >= 0 && index < fence) {
+                @SuppressWarnings("unchecked") T e = (T) array[index++];
+                action.accept(e);
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public long estimateSize() { return (long)(fence - index); }
+
+        @Override
+        public int characteristics() {
+            return characteristics;
+        }
+
+        @Override
+        public Comparator<? super T> getComparator() {
+            if (hasCharacteristics(Spliterator.SORTED))
+                return null;
+            throw new IllegalStateException();
+        }
+    }
+
+    /**
+     * A Spliterator.OfInt designed for use by sources that traverse and split
+     * elements maintained in an unmodifiable {@code int[]} array.
+     */
+    static final class IntArraySpliterator implements Spliterator.OfInt {
+        private final int[] array;
+        private int index;        // current index, modified on advance/split
+        private final int fence;  // one past last index
+        private final int characteristics;
+
+        /**
+         * Creates a spliterator covering all of the given array.
+         * @param array the array, assumed to be unmodified during use
+         * @param additionalCharacteristics Additional spliterator characteristics
+         *        of this spliterator's source or elements beyond {@code SIZED} and
+         *        {@code SUBSIZED} which are are always reported
+         */
+        public IntArraySpliterator(int[] array, int additionalCharacteristics) {
+            this(array, 0, array.length, additionalCharacteristics);
+        }
+
+        /**
+         * Creates a spliterator covering the given array and range
+         * @param array the array, assumed to be unmodified during use
+         * @param origin the least index (inclusive) to cover
+         * @param fence one past the greatest index to cover
+         * @param additionalCharacteristics Additional spliterator characteristics
+         *        of this spliterator's source or elements beyond {@code SIZED} and
+         *        {@code SUBSIZED} which are are always reported
+         */
+        public IntArraySpliterator(int[] array, int origin, int fence, int additionalCharacteristics) {
+            this.array = array;
+            this.index = origin;
+            this.fence = fence;
+            this.characteristics = additionalCharacteristics | Spliterator.SIZED | Spliterator.SUBSIZED;
+        }
+
+        @Override
+        public OfInt trySplit() {
+            int lo = index, mid = (lo + fence) >>> 1;
+            return (lo >= mid)
+                   ? null
+                   : new IntArraySpliterator(array, lo, index = mid, characteristics);
+        }
+
+        @Override
+        public void forEachRemaining(IntConsumer action) {
+            int[] a; int i, hi; // hoist accesses and checks from loop
+            if (action == null)
+                throw new NullPointerException();
+            if ((a = array).length >= (hi = fence) &&
+                (i = index) >= 0 && i < (index = hi)) {
+                do { action.accept(a[i]); } while (++i < hi);
+            }
+        }
+
+        @Override
+        public boolean tryAdvance(IntConsumer action) {
+            if (action == null)
+                throw new NullPointerException();
+            if (index >= 0 && index < fence) {
+                action.accept(array[index++]);
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public long estimateSize() { return (long)(fence - index); }
+
+        @Override
+        public int characteristics() {
+            return characteristics;
+        }
+
+        @Override
+        public Comparator<? super Integer> getComparator() {
+            if (hasCharacteristics(Spliterator.SORTED))
+                return null;
+            throw new IllegalStateException();
+        }
+    }
+
+    /**
+     * A Spliterator.OfLong designed for use by sources that traverse and split
+     * elements maintained in an unmodifiable {@code int[]} array.
+     */
+    static final class LongArraySpliterator implements Spliterator.OfLong {
+        private final long[] array;
+        private int index;        // current index, modified on advance/split
+        private final int fence;  // one past last index
+        private final int characteristics;
+
+        /**
+         * Creates a spliterator covering all of the given array.
+         * @param array the array, assumed to be unmodified during use
+         * @param additionalCharacteristics Additional spliterator characteristics
+         *        of this spliterator's source or elements beyond {@code SIZED} and
+         *        {@code SUBSIZED} which are are always reported
+         */
+        public LongArraySpliterator(long[] array, int additionalCharacteristics) {
+            this(array, 0, array.length, additionalCharacteristics);
+        }
+
+        /**
+         * Creates a spliterator covering the given array and range
+         * @param array the array, assumed to be unmodified during use
+         * @param origin the least index (inclusive) to cover
+         * @param fence one past the greatest index to cover
+         * @param additionalCharacteristics Additional spliterator characteristics
+         *        of this spliterator's source or elements beyond {@code SIZED} and
+         *        {@code SUBSIZED} which are are always reported
+         */
+        public LongArraySpliterator(long[] array, int origin, int fence, int additionalCharacteristics) {
+            this.array = array;
+            this.index = origin;
+            this.fence = fence;
+            this.characteristics = additionalCharacteristics | Spliterator.SIZED | Spliterator.SUBSIZED;
+        }
+
+        @Override
+        public OfLong trySplit() {
+            int lo = index, mid = (lo + fence) >>> 1;
+            return (lo >= mid)
+                   ? null
+                   : new LongArraySpliterator(array, lo, index = mid, characteristics);
+        }
+
+        @Override
+        public void forEachRemaining(LongConsumer action) {
+            long[] a; int i, hi; // hoist accesses and checks from loop
+            if (action == null)
+                throw new NullPointerException();
+            if ((a = array).length >= (hi = fence) &&
+                (i = index) >= 0 && i < (index = hi)) {
+                do { action.accept(a[i]); } while (++i < hi);
+            }
+        }
+
+        @Override
+        public boolean tryAdvance(LongConsumer action) {
+            if (action == null)
+                throw new NullPointerException();
+            if (index >= 0 && index < fence) {
+                action.accept(array[index++]);
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public long estimateSize() { return (long)(fence - index); }
+
+        @Override
+        public int characteristics() {
+            return characteristics;
+        }
+
+        @Override
+        public Comparator<? super Long> getComparator() {
+            if (hasCharacteristics(Spliterator.SORTED))
+                return null;
+            throw new IllegalStateException();
+        }
+    }
+
+    /**
+     * A Spliterator.OfDouble designed for use by sources that traverse and split
+     * elements maintained in an unmodifiable {@code int[]} array.
+     */
+    static final class DoubleArraySpliterator implements Spliterator.OfDouble {
+        private final double[] array;
+        private int index;        // current index, modified on advance/split
+        private final int fence;  // one past last index
+        private final int characteristics;
+
+        /**
+         * Creates a spliterator covering all of the given array.
+         * @param array the array, assumed to be unmodified during use
+         * @param additionalCharacteristics Additional spliterator characteristics
+         *        of this spliterator's source or elements beyond {@code SIZED} and
+         *        {@code SUBSIZED} which are are always reported
+         */
+        public DoubleArraySpliterator(double[] array, int additionalCharacteristics) {
+            this(array, 0, array.length, additionalCharacteristics);
+        }
+
+        /**
+         * Creates a spliterator covering the given array and range
+         * @param array the array, assumed to be unmodified during use
+         * @param origin the least index (inclusive) to cover
+         * @param fence one past the greatest index to cover
+         * @param additionalCharacteristics Additional spliterator characteristics
+         *        of this spliterator's source or elements beyond {@code SIZED} and
+         *        {@code SUBSIZED} which are are always reported
+         */
+        public DoubleArraySpliterator(double[] array, int origin, int fence, int additionalCharacteristics) {
+            this.array = array;
+            this.index = origin;
+            this.fence = fence;
+            this.characteristics = additionalCharacteristics | Spliterator.SIZED | Spliterator.SUBSIZED;
+        }
+
+        @Override
+        public OfDouble trySplit() {
+            int lo = index, mid = (lo + fence) >>> 1;
+            return (lo >= mid)
+                   ? null
+                   : new DoubleArraySpliterator(array, lo, index = mid, characteristics);
+        }
+
+        @Override
+        public void forEachRemaining(DoubleConsumer action) {
+            double[] a; int i, hi; // hoist accesses and checks from loop
+            if (action == null)
+                throw new NullPointerException();
+            if ((a = array).length >= (hi = fence) &&
+                (i = index) >= 0 && i < (index = hi)) {
+                do { action.accept(a[i]); } while (++i < hi);
+            }
+        }
+
+        @Override
+        public boolean tryAdvance(DoubleConsumer action) {
+            if (action == null)
+                throw new NullPointerException();
+            if (index >= 0 && index < fence) {
+                action.accept(array[index++]);
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public long estimateSize() { return (long)(fence - index); }
+
+        @Override
+        public int characteristics() {
+            return characteristics;
+        }
+
+        @Override
+        public Comparator<? super Double> getComparator() {
+            if (hasCharacteristics(Spliterator.SORTED))
+                return null;
+            throw new IllegalStateException();
+        }
+    }
+
+    //
+
+    /**
+     * An abstract {@code Spliterator} that implements {@code trySplit} to
+     * permit limited parallelism.
+     *
+     * <p>An extending class need only
+     * implement {@link #tryAdvance(java.util.function.Consumer) tryAdvance}.
+     * The extending class should override
+     * {@link #forEachRemaining(java.util.function.Consumer) forEach} if it can
+     * provide a more performant implementation.
+     *
+     * @apiNote
+     * This class is a useful aid for creating a spliterator when it is not
+     * possible or difficult to efficiently partition elements in a manner
+     * allowing balanced parallel computation.
+     *
+     * <p>An alternative to using this class, that also permits limited
+     * parallelism, is to create a spliterator from an iterator
+     * (see {@link #spliterator(Iterator, long, int)}.  Depending on the
+     * circumstances using an iterator may be easier or more convenient than
+     * extending this class, such as when there is already an iterator
+     * available to use.
+     *
+     * @see #spliterator(Iterator, long, int)
+     * @since 1.8
+     */
+    public static abstract class AbstractSpliterator<T> implements Spliterator<T> {
+        static final int BATCH_UNIT = 1 << 10;  // batch array size increment
+        static final int MAX_BATCH = 1 << 25;  // max batch array size;
+        private final int characteristics;
+        private long est;             // size estimate
+        private int batch;            // batch size for splits
+
+        /**
+         * Creates a spliterator reporting the given estimated size and
+         * additionalCharacteristics.
+         *
+         * @param est the estimated size of this spliterator if known, otherwise
+         *        {@code Long.MAX_VALUE}.
+         * @param additionalCharacteristics properties of this spliterator's
+         *        source or elements.  If {@code SIZED} is reported then this
+         *        spliterator will additionally report {@code SUBSIZED}.
+         */
+        protected AbstractSpliterator(long est, int additionalCharacteristics) {
+            this.est = est;
+            this.characteristics = ((additionalCharacteristics & Spliterator.SIZED) != 0)
+                                   ? additionalCharacteristics | Spliterator.SUBSIZED
+                                   : additionalCharacteristics;
+        }
+
+        static final class HoldingConsumer<T> implements Consumer<T> {
+            Object value;
+
+            @Override
+            public void accept(T value) {
+                this.value = value;
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * This implementation permits limited parallelism.
+         */
+        @Override
+        public Spliterator<T> trySplit() {
+            /*
+             * Split into arrays of arithmetically increasing batch
+             * sizes.  This will only improve parallel performance if
+             * per-element Consumer actions are more costly than
+             * transferring them into an array.  The use of an
+             * arithmetic progression in split sizes provides overhead
+             * vs parallelism bounds that do not particularly favor or
+             * penalize cases of lightweight vs heavyweight element
+             * operations, across combinations of #elements vs #cores,
+             * whether or not either are known.  We generate
+             * O(sqrt(#elements)) splits, allowing O(sqrt(#cores))
+             * potential speedup.
+             */
+            HoldingConsumer<T> holder = new HoldingConsumer<>();
+            long s = est;
+            if (s > 1 && tryAdvance(holder)) {
+                int n = batch + BATCH_UNIT;
+                if (n > s)
+                    n = (int) s;
+                if (n > MAX_BATCH)
+                    n = MAX_BATCH;
+                Object[] a = new Object[n];
+                int j = 0;
+                do { a[j] = holder.value; } while (++j < n && tryAdvance(holder));
+                batch = j;
+                if (est != Long.MAX_VALUE)
+                    est -= j;
+                return new ArraySpliterator<>(a, 0, j, characteristics());
+            }
+            return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * @implSpec
+         * This implementation returns the estimated size as reported when
+         * created and, if the estimate size is known, decreases in size when
+         * split.
+         */
+        @Override
+        public long estimateSize() {
+            return est;
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * @implSpec
+         * This implementation returns the characteristics as reported when
+         * created.
+         */
+        @Override
+        public int characteristics() {
+            return characteristics;
+        }
+    }
+
+    /**
+     * An abstract {@code Spliterator.OfInt} that implements {@code trySplit} to
+     * permit limited parallelism.
+     *
+     * <p>To implement a spliterator an extending class need only
+     * implement {@link #tryAdvance(java.util.function.IntConsumer)}
+     * tryAdvance}.  The extending class should override
+     * {@link #forEachRemaining(java.util.function.IntConsumer)} forEach} if it
+     * can provide a more performant implementation.
+     *
+     * @apiNote
+     * This class is a useful aid for creating a spliterator when it is not
+     * possible or difficult to efficiently partition elements in a manner
+     * allowing balanced parallel computation.
+     *
+     * <p>An alternative to using this class, that also permits limited
+     * parallelism, is to create a spliterator from an iterator
+     * (see {@link #spliterator(java.util.PrimitiveIterator.OfInt, long, int)}.
+     * Depending on the circumstances using an iterator may be easier or more
+     * convenient than extending this class. For example, if there is already an
+     * iterator available to use then there is no need to extend this class.
+     *
+     * @see #spliterator(java.util.PrimitiveIterator.OfInt, long, int)
+     * @since 1.8
+     */
+    public static abstract class AbstractIntSpliterator implements Spliterator.OfInt {
+        static final int MAX_BATCH = AbstractSpliterator.MAX_BATCH;
+        static final int BATCH_UNIT = AbstractSpliterator.BATCH_UNIT;
+        private final int characteristics;
+        private long est;             // size estimate
+        private int batch;            // batch size for splits
+
+        /**
+         * Creates a spliterator reporting the given estimated size and
+         * characteristics.
+         *
+         * @param est the estimated size of this spliterator if known, otherwise
+         *        {@code Long.MAX_VALUE}.
+         * @param additionalCharacteristics properties of this spliterator's
+         *        source or elements.  If {@code SIZED} is reported then this
+         *        spliterator will additionally report {@code SUBSIZED}.
+         */
+        protected AbstractIntSpliterator(long est, int additionalCharacteristics) {
+            this.est = est;
+            this.characteristics = ((additionalCharacteristics & Spliterator.SIZED) != 0)
+                                   ? additionalCharacteristics | Spliterator.SUBSIZED
+                                   : additionalCharacteristics;
+        }
+
+        static final class HoldingIntConsumer implements IntConsumer {
+            int value;
+
+            @Override
+            public void accept(int value) {
+                this.value = value;
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * This implementation permits limited parallelism.
+         */
+        @Override
+        public Spliterator.OfInt trySplit() {
+            HoldingIntConsumer holder = new HoldingIntConsumer();
+            long s = est;
+            if (s > 1 && tryAdvance(holder)) {
+                int n = batch + BATCH_UNIT;
+                if (n > s)
+                    n = (int) s;
+                if (n > MAX_BATCH)
+                    n = MAX_BATCH;
+                int[] a = new int[n];
+                int j = 0;
+                do { a[j] = holder.value; } while (++j < n && tryAdvance(holder));
+                batch = j;
+                if (est != Long.MAX_VALUE)
+                    est -= j;
+                return new IntArraySpliterator(a, 0, j, characteristics());
+            }
+            return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * @implSpec
+         * This implementation returns the estimated size as reported when
+         * created and, if the estimate size is known, decreases in size when
+         * split.
+         */
+        @Override
+        public long estimateSize() {
+            return est;
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * @implSpec
+         * This implementation returns the characteristics as reported when
+         * created.
+         */
+        @Override
+        public int characteristics() {
+            return characteristics;
+        }
+    }
+
+    /**
+     * An abstract {@code Spliterator.OfLong} that implements {@code trySplit}
+     * to permit limited parallelism.
+     *
+     * <p>To implement a spliterator an extending class need only
+     * implement {@link #tryAdvance(java.util.function.LongConsumer)}
+     * tryAdvance}.  The extending class should override
+     * {@link #forEachRemaining(java.util.function.LongConsumer)} forEach} if it
+     * can provide a more performant implementation.
+     *
+     * @apiNote
+     * This class is a useful aid for creating a spliterator when it is not
+     * possible or difficult to efficiently partition elements in a manner
+     * allowing balanced parallel computation.
+     *
+     * <p>An alternative to using this class, that also permits limited
+     * parallelism, is to create a spliterator from an iterator
+     * (see {@link #spliterator(java.util.PrimitiveIterator.OfLong, long, int)}.
+     * Depending on the circumstances using an iterator may be easier or more
+     * convenient than extending this class. For example, if there is already an
+     * iterator available to use then there is no need to extend this class.
+     *
+     * @see #spliterator(java.util.PrimitiveIterator.OfLong, long, int)
+     * @since 1.8
+     */
+    public static abstract class AbstractLongSpliterator implements Spliterator.OfLong {
+        static final int MAX_BATCH = AbstractSpliterator.MAX_BATCH;
+        static final int BATCH_UNIT = AbstractSpliterator.BATCH_UNIT;
+        private final int characteristics;
+        private long est;             // size estimate
+        private int batch;            // batch size for splits
+
+        /**
+         * Creates a spliterator reporting the given estimated size and
+         * characteristics.
+         *
+         * @param est the estimated size of this spliterator if known, otherwise
+         *        {@code Long.MAX_VALUE}.
+         * @param additionalCharacteristics properties of this spliterator's
+         *        source or elements.  If {@code SIZED} is reported then this
+         *        spliterator will additionally report {@code SUBSIZED}.
+         */
+        protected AbstractLongSpliterator(long est, int additionalCharacteristics) {
+            this.est = est;
+            this.characteristics = ((additionalCharacteristics & Spliterator.SIZED) != 0)
+                                   ? additionalCharacteristics | Spliterator.SUBSIZED
+                                   : additionalCharacteristics;
+        }
+
+        static final class HoldingLongConsumer implements LongConsumer {
+            long value;
+
+            @Override
+            public void accept(long value) {
+                this.value = value;
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * This implementation permits limited parallelism.
+         */
+        @Override
+        public Spliterator.OfLong trySplit() {
+            HoldingLongConsumer holder = new HoldingLongConsumer();
+            long s = est;
+            if (s > 1 && tryAdvance(holder)) {
+                int n = batch + BATCH_UNIT;
+                if (n > s)
+                    n = (int) s;
+                if (n > MAX_BATCH)
+                    n = MAX_BATCH;
+                long[] a = new long[n];
+                int j = 0;
+                do { a[j] = holder.value; } while (++j < n && tryAdvance(holder));
+                batch = j;
+                if (est != Long.MAX_VALUE)
+                    est -= j;
+                return new LongArraySpliterator(a, 0, j, characteristics());
+            }
+            return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * @implSpec
+         * This implementation returns the estimated size as reported when
+         * created and, if the estimate size is known, decreases in size when
+         * split.
+         */
+        @Override
+        public long estimateSize() {
+            return est;
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * @implSpec
+         * This implementation returns the characteristics as reported when
+         * created.
+         */
+        @Override
+        public int characteristics() {
+            return characteristics;
+        }
+    }
+
+    /**
+     * An abstract {@code Spliterator.OfDouble} that implements
+     * {@code trySplit} to permit limited parallelism.
+     *
+     * <p>To implement a spliterator an extending class need only
+     * implement {@link #tryAdvance(java.util.function.DoubleConsumer)}
+     * tryAdvance}.  The extending class should override
+     * {@link #forEachRemaining(java.util.function.DoubleConsumer)} forEach} if
+     * it can provide a more performant implementation.
+     *
+     * @apiNote
+     * This class is a useful aid for creating a spliterator when it is not
+     * possible or difficult to efficiently partition elements in a manner
+     * allowing balanced parallel computation.
+     *
+     * <p>An alternative to using this class, that also permits limited
+     * parallelism, is to create a spliterator from an iterator
+     * (see {@link #spliterator(java.util.PrimitiveIterator.OfDouble, long, int)}.
+     * Depending on the circumstances using an iterator may be easier or more
+     * convenient than extending this class. For example, if there is already an
+     * iterator available to use then there is no need to extend this class.
+     *
+     * @see #spliterator(java.util.PrimitiveIterator.OfDouble, long, int)
+     * @since 1.8
+     */
+    public static abstract class AbstractDoubleSpliterator implements Spliterator.OfDouble {
+        static final int MAX_BATCH = AbstractSpliterator.MAX_BATCH;
+        static final int BATCH_UNIT = AbstractSpliterator.BATCH_UNIT;
+        private final int characteristics;
+        private long est;             // size estimate
+        private int batch;            // batch size for splits
+
+        /**
+         * Creates a spliterator reporting the given estimated size and
+         * characteristics.
+         *
+         * @param est the estimated size of this spliterator if known, otherwise
+         *        {@code Long.MAX_VALUE}.
+         * @param additionalCharacteristics properties of this spliterator's
+         *        source or elements.  If {@code SIZED} is reported then this
+         *        spliterator will additionally report {@code SUBSIZED}.
+         */
+        protected AbstractDoubleSpliterator(long est, int additionalCharacteristics) {
+            this.est = est;
+            this.characteristics = ((additionalCharacteristics & Spliterator.SIZED) != 0)
+                                   ? additionalCharacteristics | Spliterator.SUBSIZED
+                                   : additionalCharacteristics;
+        }
+
+        static final class HoldingDoubleConsumer implements DoubleConsumer {
+            double value;
+
+            @Override
+            public void accept(double value) {
+                this.value = value;
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * This implementation permits limited parallelism.
+         */
+        @Override
+        public Spliterator.OfDouble trySplit() {
+            HoldingDoubleConsumer holder = new HoldingDoubleConsumer();
+            long s = est;
+            if (s > 1 && tryAdvance(holder)) {
+                int n = batch + BATCH_UNIT;
+                if (n > s)
+                    n = (int) s;
+                if (n > MAX_BATCH)
+                    n = MAX_BATCH;
+                double[] a = new double[n];
+                int j = 0;
+                do { a[j] = holder.value; } while (++j < n && tryAdvance(holder));
+                batch = j;
+                if (est != Long.MAX_VALUE)
+                    est -= j;
+                return new DoubleArraySpliterator(a, 0, j, characteristics());
+            }
+            return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * @implSpec
+         * This implementation returns the estimated size as reported when
+         * created and, if the estimate size is known, decreases in size when
+         * split.
+         */
+        @Override
+        public long estimateSize() {
+            return est;
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * @implSpec
+         * This implementation returns the characteristics as reported when
+         * created.
+         */
+        @Override
+        public int characteristics() {
+            return characteristics;
+        }
+    }
+
+    // Iterator-based Spliterators
+
+    /**
+     * A Spliterator using a given Iterator for element
+     * operations. The spliterator implements {@code trySplit} to
+     * permit limited parallelism.
+     */
+    static class IteratorSpliterator<T> implements Spliterator<T> {
+        static final int BATCH_UNIT = 1 << 10;  // batch array size increment
+        static final int MAX_BATCH = 1 << 25;  // max batch array size;
+        private final Collection<? extends T> collection; // null OK
+        private Iterator<? extends T> it;
+        private final int characteristics;
+        private long est;             // size estimate
+        private int batch;            // batch size for splits
+
+        /**
+         * Creates a spliterator using the given given
+         * collection's {@link java.util.Collection#iterator()) for traversal,
+         * and reporting its {@link java.util.Collection#size()) as its initial
+         * size.
+         *
+         * @param c the collection
+         * @param characteristics properties of this spliterator's
+         *        source or elements.
+         */
+        public IteratorSpliterator(Collection<? extends T> collection, int characteristics) {
+            this.collection = collection;
+            this.it = null;
+            this.characteristics = (characteristics & Spliterator.CONCURRENT) == 0
+                                   ? characteristics | Spliterator.SIZED | Spliterator.SUBSIZED
+                                   : characteristics;
+        }
+
+        /**
+         * Creates a spliterator using the given iterator
+         * for traversal, and reporting the given initial size
+         * and characteristics.
+         *
+         * @param iterator the iterator for the source
+         * @param size the number of elements in the source
+         * @param characteristics properties of this spliterator's
+         * source or elements.
+         */
+        public IteratorSpliterator(Iterator<? extends T> iterator, long size, int characteristics) {
+            this.collection = null;
+            this.it = iterator;
+            this.est = size;
+            this.characteristics = (characteristics & Spliterator.CONCURRENT) == 0
+                                   ? characteristics | Spliterator.SIZED | Spliterator.SUBSIZED
+                                   : characteristics;
+        }
+
+        /**
+         * Creates a spliterator using the given iterator
+         * for traversal, and reporting the given initial size
+         * and characteristics.
+         *
+         * @param iterator the iterator for the source
+         * @param characteristics properties of this spliterator's
+         * source or elements.
+         */
+        public IteratorSpliterator(Iterator<? extends T> iterator, int characteristics) {
+            this.collection = null;
+            this.it = iterator;
+            this.est = Long.MAX_VALUE;
+            this.characteristics = characteristics & ~(Spliterator.SIZED | Spliterator.SUBSIZED);
+        }
+
+        @Override
+        public Spliterator<T> trySplit() {
+            /*
+             * Split into arrays of arithmetically increasing batch
+             * sizes.  This will only improve parallel performance if
+             * per-element Consumer actions are more costly than
+             * transferring them into an array.  The use of an
+             * arithmetic progression in split sizes provides overhead
+             * vs parallelism bounds that do not particularly favor or
+             * penalize cases of lightweight vs heavyweight element
+             * operations, across combinations of #elements vs #cores,
+             * whether or not either are known.  We generate
+             * O(sqrt(#elements)) splits, allowing O(sqrt(#cores))
+             * potential speedup.
+             */
+            Iterator<? extends T> i;
+            long s;
+            if ((i = it) == null) {
+                i = it = collection.iterator();
+                s = est = (long) collection.size();
+            }
+            else
+                s = est;
+            if (s > 1 && i.hasNext()) {
+                int n = batch + BATCH_UNIT;
+                if (n > s)
+                    n = (int) s;
+                if (n > MAX_BATCH)
+                    n = MAX_BATCH;
+                Object[] a = new Object[n];
+                int j = 0;
+                do { a[j] = i.next(); } while (++j < n && i.hasNext());
+                batch = j;
+                if (est != Long.MAX_VALUE)
+                    est -= j;
+                return new ArraySpliterator<>(a, 0, j, characteristics);
+            }
+            return null;
+        }
+
+        @Override
+        public void forEachRemaining(Consumer<? super T> action) {
+            if (action == null) throw new NullPointerException();
+            Iterator<? extends T> i;
+            if ((i = it) == null) {
+                i = it = collection.iterator();
+                est = (long)collection.size();
+            }
+            i.forEachRemaining(action);
+        }
+
+        @Override
+        public boolean tryAdvance(Consumer<? super T> action) {
+            if (action == null) throw new NullPointerException();
+            if (it == null) {
+                it = collection.iterator();
+                est = (long) collection.size();
+            }
+            if (it.hasNext()) {
+                action.accept(it.next());
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public long estimateSize() {
+            if (it == null) {
+                it = collection.iterator();
+                return est = (long)collection.size();
+            }
+            return est;
+        }
+
+        @Override
+        public int characteristics() { return characteristics; }
+
+        @Override
+        public Comparator<? super T> getComparator() {
+            if (hasCharacteristics(Spliterator.SORTED))
+                return null;
+            throw new IllegalStateException();
+        }
+    }
+
+    /**
+     * A Spliterator.OfInt using a given IntStream.IntIterator for element
+     * operations. The spliterator implements {@code trySplit} to
+     * permit limited parallelism.
+     */
+    static final class IntIteratorSpliterator implements Spliterator.OfInt {
+        static final int BATCH_UNIT = IteratorSpliterator.BATCH_UNIT;
+        static final int MAX_BATCH = IteratorSpliterator.MAX_BATCH;
+        private PrimitiveIterator.OfInt it;
+        private final int characteristics;
+        private long est;             // size estimate
+        private int batch;            // batch size for splits
+
+        /**
+         * Creates a spliterator using the given iterator
+         * for traversal, and reporting the given initial size
+         * and characteristics.
+         *
+         * @param iterator the iterator for the source
+         * @param size the number of elements in the source
+         * @param characteristics properties of this spliterator's
+         * source or elements.
+         */
+        public IntIteratorSpliterator(PrimitiveIterator.OfInt iterator, long size, int characteristics) {
+            this.it = iterator;
+            this.est = size;
+            this.characteristics = (characteristics & Spliterator.CONCURRENT) == 0
+                                   ? characteristics | Spliterator.SIZED | Spliterator.SUBSIZED
+                                   : characteristics;
+        }
+
+        /**
+         * Creates a spliterator using the given iterator for a
+         * source of unknown size, reporting the given
+         * characteristics.
+         *
+         * @param iterator the iterator for the source
+         * @param characteristics properties of this spliterator's
+         * source or elements.
+         */
+        public IntIteratorSpliterator(PrimitiveIterator.OfInt iterator, int characteristics) {
+            this.it = iterator;
+            this.est = Long.MAX_VALUE;
+            this.characteristics = characteristics & ~(Spliterator.SIZED | Spliterator.SUBSIZED);
+        }
+
+        @Override
+        public OfInt trySplit() {
+            PrimitiveIterator.OfInt i = it;
+            long s = est;
+            if (s > 1 && i.hasNext()) {
+                int n = batch + BATCH_UNIT;
+                if (n > s)
+                    n = (int) s;
+                if (n > MAX_BATCH)
+                    n = MAX_BATCH;
+                int[] a = new int[n];
+                int j = 0;
+                do { a[j] = i.nextInt(); } while (++j < n && i.hasNext());
+                batch = j;
+                if (est != Long.MAX_VALUE)
+                    est -= j;
+                return new IntArraySpliterator(a, 0, j, characteristics);
+            }
+            return null;
+        }
+
+        @Override
+        public void forEachRemaining(IntConsumer action) {
+            if (action == null) throw new NullPointerException();
+            it.forEachRemaining(action);
+        }
+
+        @Override
+        public boolean tryAdvance(IntConsumer action) {
+            if (action == null) throw new NullPointerException();
+            if (it.hasNext()) {
+                action.accept(it.nextInt());
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public long estimateSize() {
+            return est;
+        }
+
+        @Override
+        public int characteristics() { return characteristics; }
+
+        @Override
+        public Comparator<? super Integer> getComparator() {
+            if (hasCharacteristics(Spliterator.SORTED))
+                return null;
+            throw new IllegalStateException();
+        }
+    }
+
+    static final class LongIteratorSpliterator implements Spliterator.OfLong {
+        static final int BATCH_UNIT = IteratorSpliterator.BATCH_UNIT;
+        static final int MAX_BATCH = IteratorSpliterator.MAX_BATCH;
+        private PrimitiveIterator.OfLong it;
+        private final int characteristics;
+        private long est;             // size estimate
+        private int batch;            // batch size for splits
+
+        /**
+         * Creates a spliterator using the given iterator
+         * for traversal, and reporting the given initial size
+         * and characteristics.
+         *
+         * @param iterator the iterator for the source
+         * @param size the number of elements in the source
+         * @param characteristics properties of this spliterator's
+         * source or elements.
+         */
+        public LongIteratorSpliterator(PrimitiveIterator.OfLong iterator, long size, int characteristics) {
+            this.it = iterator;
+            this.est = size;
+            this.characteristics = (characteristics & Spliterator.CONCURRENT) == 0
+                                   ? characteristics | Spliterator.SIZED | Spliterator.SUBSIZED
+                                   : characteristics;
+        }
+
+        /**
+         * Creates a spliterator using the given iterator for a
+         * source of unknown size, reporting the given
+         * characteristics.
+         *
+         * @param iterator the iterator for the source
+         * @param characteristics properties of this spliterator's
+         * source or elements.
+         */
+        public LongIteratorSpliterator(PrimitiveIterator.OfLong iterator, int characteristics) {
+            this.it = iterator;
+            this.est = Long.MAX_VALUE;
+            this.characteristics = characteristics & ~(Spliterator.SIZED | Spliterator.SUBSIZED);
+        }
+
+        @Override
+        public OfLong trySplit() {
+            PrimitiveIterator.OfLong i = it;
+            long s = est;
+            if (s > 1 && i.hasNext()) {
+                int n = batch + BATCH_UNIT;
+                if (n > s)
+                    n = (int) s;
+                if (n > MAX_BATCH)
+                    n = MAX_BATCH;
+                long[] a = new long[n];
+                int j = 0;
+                do { a[j] = i.nextLong(); } while (++j < n && i.hasNext());
+                batch = j;
+                if (est != Long.MAX_VALUE)
+                    est -= j;
+                return new LongArraySpliterator(a, 0, j, characteristics);
+            }
+            return null;
+        }
+
+        @Override
+        public void forEachRemaining(LongConsumer action) {
+            if (action == null) throw new NullPointerException();
+            it.forEachRemaining(action);
+        }
+
+        @Override
+        public boolean tryAdvance(LongConsumer action) {
+            if (action == null) throw new NullPointerException();
+            if (it.hasNext()) {
+                action.accept(it.nextLong());
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public long estimateSize() {
+            return est;
+        }
+
+        @Override
+        public int characteristics() { return characteristics; }
+
+        @Override
+        public Comparator<? super Long> getComparator() {
+            if (hasCharacteristics(Spliterator.SORTED))
+                return null;
+            throw new IllegalStateException();
+        }
+    }
+
+    static final class DoubleIteratorSpliterator implements Spliterator.OfDouble {
+        static final int BATCH_UNIT = IteratorSpliterator.BATCH_UNIT;
+        static final int MAX_BATCH = IteratorSpliterator.MAX_BATCH;
+        private PrimitiveIterator.OfDouble it;
+        private final int characteristics;
+        private long est;             // size estimate
+        private int batch;            // batch size for splits
+
+        /**
+         * Creates a spliterator using the given iterator
+         * for traversal, and reporting the given initial size
+         * and characteristics.
+         *
+         * @param iterator the iterator for the source
+         * @param size the number of elements in the source
+         * @param characteristics properties of this spliterator's
+         * source or elements.
+         */
+        public DoubleIteratorSpliterator(PrimitiveIterator.OfDouble iterator, long size, int characteristics) {
+            this.it = iterator;
+            this.est = size;
+            this.characteristics = (characteristics & Spliterator.CONCURRENT) == 0
+                                   ? characteristics | Spliterator.SIZED | Spliterator.SUBSIZED
+                                   : characteristics;
+        }
+
+        /**
+         * Creates a spliterator using the given iterator for a
+         * source of unknown size, reporting the given
+         * characteristics.
+         *
+         * @param iterator the iterator for the source
+         * @param characteristics properties of this spliterator's
+         * source or elements.
+         */
+        public DoubleIteratorSpliterator(PrimitiveIterator.OfDouble iterator, int characteristics) {
+            this.it = iterator;
+            this.est = Long.MAX_VALUE;
+            this.characteristics = characteristics & ~(Spliterator.SIZED | Spliterator.SUBSIZED);
+        }
+
+        @Override
+        public OfDouble trySplit() {
+            PrimitiveIterator.OfDouble i = it;
+            long s = est;
+            if (s > 1 && i.hasNext()) {
+                int n = batch + BATCH_UNIT;
+                if (n > s)
+                    n = (int) s;
+                if (n > MAX_BATCH)
+                    n = MAX_BATCH;
+                double[] a = new double[n];
+                int j = 0;
+                do { a[j] = i.nextDouble(); } while (++j < n && i.hasNext());
+                batch = j;
+                if (est != Long.MAX_VALUE)
+                    est -= j;
+                return new DoubleArraySpliterator(a, 0, j, characteristics);
+            }
+            return null;
+        }
+
+        @Override
+        public void forEachRemaining(DoubleConsumer action) {
+            if (action == null) throw new NullPointerException();
+            it.forEachRemaining(action);
+        }
+
+        @Override
+        public boolean tryAdvance(DoubleConsumer action) {
+            if (action == null) throw new NullPointerException();
+            if (it.hasNext()) {
+                action.accept(it.nextDouble());
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public long estimateSize() {
+            return est;
+        }
+
+        @Override
+        public int characteristics() { return characteristics; }
+
+        @Override
+        public Comparator<? super Double> getComparator() {
+            if (hasCharacteristics(Spliterator.SORTED))
+                return null;
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/ojluni/src/main/java/java/util/TreeMap.java b/ojluni/src/main/java/java/util/TreeMap.java
index d9235a9..7a67e01 100755
--- a/ojluni/src/main/java/java/util/TreeMap.java
+++ b/ojluni/src/main/java/java/util/TreeMap.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,9 @@
 
 package java.util;
 
+import java.io.Serializable;
 import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
 import java.util.function.Consumer;
 
 /**
@@ -311,7 +313,7 @@
     public void putAll(Map<? extends K, ? extends V> map) {
         int mapSize = map.size();
         if (size==0 && mapSize!=0 && map instanceof SortedMap) {
-            Comparator c = ((SortedMap)map).comparator();
+            Comparator<?> c = ((SortedMap<?,?>)map).comparator();
             if (c == comparator || (c != null && c.equals(comparator))) {
                 ++modCount;
                 try {
@@ -344,7 +346,8 @@
             return getEntryUsingComparator(key);
         if (key == null)
             throw new NullPointerException();
-        Comparable<? super K> k = (Comparable<? super K>) key;
+        @SuppressWarnings("unchecked")
+            Comparable<? super K> k = (Comparable<? super K>) key;
         TreeMapEntry<K,V> p = root;
         while (p != null) {
             int cmp = k.compareTo(p.key);
@@ -365,7 +368,8 @@
      * worthwhile here.)
      */
     final TreeMapEntry<K,V> getEntryUsingComparator(Object key) {
-        K k = (K) key;
+        @SuppressWarnings("unchecked")
+            K k = (K) key;
         Comparator<? super K> cpr = comparator;
         if (cpr != null) {
             TreeMapEntry<K,V> p = root;
@@ -582,7 +586,8 @@
         else {
             if (key == null)
                 throw new NullPointerException();
-            Comparable<? super K> k = (Comparable<? super K>) key;
+            @SuppressWarnings("unchecked")
+                Comparable<? super K> k = (Comparable<? super K>) key;
             do {
                 parent = t;
                 cmp = k.compareTo(t.key);
@@ -646,11 +651,11 @@
      * @return a shallow copy of this map
      */
     public Object clone() {
-        TreeMap<K,V> clone = null;
+        TreeMap<?,?> clone;
         try {
-            clone = (TreeMap<K,V>) super.clone();
+            clone = (TreeMap<?,?>) super.clone();
         } catch (CloneNotSupportedException e) {
-            throw new InternalError();
+            throw new InternalError(e);
         }
 
         // Put clone into "virgin" state (except for comparator)
@@ -810,8 +815,19 @@
 
     /**
      * Returns a {@link Set} view of the keys contained in this map.
-     * The set's iterator returns the keys in ascending order.
-     * The set is backed by the map, so changes to the map are
+     *
+     * <p>The set's iterator returns the keys in ascending order.
+     * The set's spliterator is
+     * <em><a href="Spliterator.html#binding">late-binding</a></em>,
+     * <em>fail-fast</em>, and additionally reports {@link Spliterator#SORTED}
+     * and {@link Spliterator#ORDERED} with an encounter order that is ascending
+     * key order.  The spliterator's comparator (see
+     * {@link java.util.Spliterator#getComparator()}) is {@code null} if
+     * the tree map's comparator (see {@link #comparator()}) is {@code null}.
+     * Otherwise, the spliterator's comparator is the same as or imposes the
+     * same total ordering as the tree map's comparator.
+     *
+     * <p>The set is backed by the map, so changes to the map are
      * reflected in the set, and vice-versa.  If the map is modified
      * while an iteration over the set is in progress (except through
      * the iterator's own {@code remove} operation), the results of
@@ -831,7 +847,7 @@
      */
     public NavigableSet<K> navigableKeySet() {
         KeySet<K> nks = navigableKeySet;
-        return (nks != null) ? nks : (navigableKeySet = new KeySet(this));
+        return (nks != null) ? nks : (navigableKeySet = new KeySet<>(this));
     }
 
     /**
@@ -843,9 +859,15 @@
 
     /**
      * Returns a {@link Collection} view of the values contained in this map.
-     * The collection's iterator returns the values in ascending order
-     * of the corresponding keys.
-     * The collection is backed by the map, so changes to the map are
+     *
+     * <p>The collection's iterator returns the values in ascending order
+     * of the corresponding keys. The collection's spliterator is
+     * <em><a href="Spliterator.html#binding">late-binding</a></em>,
+     * <em>fail-fast</em>, and additionally reports {@link Spliterator#ORDERED}
+     * with an encounter order that is ascending order of the corresponding
+     * keys.
+     *
+     * <p>The collection is backed by the map, so changes to the map are
      * reflected in the collection, and vice-versa.  If the map is
      * modified while an iteration over the collection is in progress
      * (except through the iterator's own {@code remove} operation),
@@ -863,8 +885,15 @@
 
     /**
      * Returns a {@link Set} view of the mappings contained in this map.
-     * The set's iterator returns the entries in ascending key order.
-     * The set is backed by the map, so changes to the map are
+     *
+     * <p>The set's iterator returns the entries in ascending key order. The
+     * sets's spliterator is
+     * <em><a href="Spliterator.html#binding">late-binding</a></em>,
+     * <em>fail-fast</em>, and additionally reports {@link Spliterator#SORTED} and
+     * {@link Spliterator#ORDERED} with an encounter order that is ascending key
+     * order.
+     *
+     * <p>The set is backed by the map, so changes to the map are
      * reflected in the set, and vice-versa.  If the map is modified
      * while an iteration over the set is in progress (except through
      * the iterator's own {@code remove} operation, or through the
@@ -887,9 +916,9 @@
     public NavigableMap<K, V> descendingMap() {
         NavigableMap<K, V> km = descendingMap;
         return (km != null) ? km :
-            (descendingMap = new DescendingSubMap(this,
-                                                  true, null, true,
-                                                  true, null, true));
+            (descendingMap = new DescendingSubMap<>(this,
+                                                    true, null, true,
+                                                    true, null, true));
     }
 
     /**
@@ -902,9 +931,9 @@
      */
     public NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive,
                                     K toKey,   boolean toInclusive) {
-        return new AscendingSubMap(this,
-                                   false, fromKey, fromInclusive,
-                                   false, toKey,   toInclusive);
+        return new AscendingSubMap<>(this,
+                                     false, fromKey, fromInclusive,
+                                     false, toKey,   toInclusive);
     }
 
     /**
@@ -916,9 +945,9 @@
      * @since 1.6
      */
     public NavigableMap<K,V> headMap(K toKey, boolean inclusive) {
-        return new AscendingSubMap(this,
-                                   true,  null,  true,
-                                   false, toKey, inclusive);
+        return new AscendingSubMap<>(this,
+                                     true,  null,  true,
+                                     false, toKey, inclusive);
     }
 
     /**
@@ -930,9 +959,9 @@
      * @since 1.6
      */
     public NavigableMap<K,V> tailMap(K fromKey, boolean inclusive) {
-        return new AscendingSubMap(this,
-                                   false, fromKey, inclusive,
-                                   true,  null,    true);
+        return new AscendingSubMap<>(this,
+                                     false, fromKey, inclusive,
+                                     true,  null,    true);
     }
 
     /**
@@ -1009,6 +1038,10 @@
         public void clear() {
             TreeMap.this.clear();
         }
+
+        public Spliterator<V> spliterator() {
+            return new ValueSpliterator<K,V>(TreeMap.this, null, null, 0, -1, 0);
+        }
     }
 
     class EntrySet extends AbstractSet<Map.Entry<K,V>> {
@@ -1045,6 +1078,10 @@
         public void clear() {
             TreeMap.this.clear();
         }
+
+        public Spliterator<Map.Entry<K,V>> spliterator() {
+            return new EntrySpliterator<K,V>(TreeMap.this, null, null, 0, -1, 0);
+        }
     }
 
     /*
@@ -1064,21 +1101,21 @@
     }
 
     static final class KeySet<E> extends AbstractSet<E> implements NavigableSet<E> {
-        private final NavigableMap<E, Object> m;
-        KeySet(NavigableMap<E,Object> map) { m = map; }
+        private final NavigableMap<E, ?> m;
+        KeySet(NavigableMap<E,?> map) { m = map; }
 
         public Iterator<E> iterator() {
             if (m instanceof TreeMap)
-                return ((TreeMap<E,Object>)m).keyIterator();
+                return ((TreeMap<E,?>)m).keyIterator();
             else
-                return (Iterator<E>)(((TreeMap.NavigableSubMap)m).keyIterator());
+                return ((TreeMap.NavigableSubMap<E,?>)m).keyIterator();
         }
 
         public Iterator<E> descendingIterator() {
             if (m instanceof TreeMap)
-                return ((TreeMap<E,Object>)m).descendingKeyIterator();
+                return ((TreeMap<E,?>)m).descendingKeyIterator();
             else
-                return (Iterator<E>)(((TreeMap.NavigableSubMap)m).descendingKeyIterator());
+                return ((TreeMap.NavigableSubMap<E,?>)m).descendingKeyIterator();
         }
 
         public int size() { return m.size(); }
@@ -1093,11 +1130,11 @@
         public E last() { return m.lastKey(); }
         public Comparator<? super E> comparator() { return m.comparator(); }
         public E pollFirst() {
-            Map.Entry<E,Object> e = m.pollFirstEntry();
+            Map.Entry<E,?> e = m.pollFirstEntry();
             return (e == null) ? null : e.getKey();
         }
         public E pollLast() {
-            Map.Entry<E,Object> e = m.pollLastEntry();
+            Map.Entry<E,?> e = m.pollLastEntry();
             return (e == null) ? null : e.getKey();
         }
         public boolean remove(Object o) {
@@ -1126,7 +1163,11 @@
             return tailSet(fromElement, true);
         }
         public NavigableSet<E> descendingSet() {
-            return new KeySet(m.descendingMap());
+            return new KeySet<>(m.descendingMap());
+        }
+
+        public Spliterator<E> spliterator() {
+            return keySpliteratorFor(m);
         }
     }
 
@@ -1218,6 +1259,15 @@
         public K next() {
             return prevEntry().key;
         }
+        public void remove() {
+            if (lastReturned == null)
+                throw new IllegalStateException();
+            if (modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+            deleteEntry(lastReturned);
+            lastReturned = null;
+            expectedModCount = modCount;
+        }
     }
 
     // Little utilities
@@ -1225,6 +1275,7 @@
     /**
      * Compares two keys using the correct comparison method for this TreeMap.
      */
+    @SuppressWarnings("unchecked")
     final int compare(Object k1, Object k2) {
         return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
             : comparator.compare((K)k1, (K)k2);
@@ -1426,6 +1477,8 @@
         /** Returns ascending iterator from the perspective of this submap */
         abstract Iterator<K> keyIterator();
 
+        abstract Spliterator<K> keySpliterator();
+
         /** Returns descending iterator from the perspective of this submap */
         abstract Iterator<K> descendingKeyIterator();
 
@@ -1529,7 +1582,7 @@
         public final NavigableSet<K> navigableKeySet() {
             KeySet<K> nksv = navigableKeySetView;
             return (nksv != null) ? nksv :
-                (navigableKeySetView = new TreeMap.KeySet(this));
+                (navigableKeySetView = new TreeMap.KeySet<>(this));
         }
 
         public final Set<K> keySet() {
@@ -1563,7 +1616,7 @@
                 if (size == -1 || sizeModCount != m.modCount) {
                     sizeModCount = m.modCount;
                     size = 0;
-                    Iterator i = iterator();
+                    Iterator<?> i = iterator();
                     while (i.hasNext()) {
                         size++;
                         i.next();
@@ -1580,11 +1633,11 @@
             public boolean contains(Object o) {
                 if (!(o instanceof Map.Entry))
                     return false;
-                Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
-                K key = entry.getKey();
+                Map.Entry<?,?> entry = (Map.Entry<?,?>) o;
+                Object key = entry.getKey();
                 if (!inRange(key))
                     return false;
-                TreeMapEntry node = m.getEntry(key);
+                TreeMapEntry<?, ?> node = m.getEntry(key);
                 return node != null &&
                     valEquals(node.getValue(), entry.getValue());
             }
@@ -1592,8 +1645,8 @@
             public boolean remove(Object o) {
                 if (!(o instanceof Map.Entry))
                     return false;
-                Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
-                K key = entry.getKey();
+                Map.Entry<?,?> entry = (Map.Entry<?,?>) o;
+                Object key = entry.getKey();
                 if (!inRange(key))
                     return false;
                 TreeMapEntry<K,V> node = m.getEntry(key);
@@ -1687,19 +1740,6 @@
             }
         }
 
-        final class SubMapKeyIterator extends SubMapIterator<K> {
-            SubMapKeyIterator(TreeMapEntry<K,V> first,
-                              TreeMapEntry<K,V> fence) {
-                super(first, fence);
-            }
-            public K next() {
-                return nextEntry().key;
-            }
-            public void remove() {
-                removeAscending();
-            }
-        }
-
         final class DescendingSubMapEntryIterator extends SubMapIterator<Map.Entry<K,V>> {
             DescendingSubMapEntryIterator(TreeMapEntry<K,V> last,
                                           TreeMapEntry<K,V> fence) {
@@ -1714,7 +1754,47 @@
             }
         }
 
-        final class DescendingSubMapKeyIterator extends SubMapIterator<K> {
+        // Implement minimal Spliterator as KeySpliterator backup
+        final class SubMapKeyIterator extends SubMapIterator<K>
+            implements Spliterator<K> {
+            SubMapKeyIterator(TreeMapEntry<K,V> first,
+                              TreeMapEntry<K,V> fence) {
+                super(first, fence);
+            }
+            public K next() {
+                return nextEntry().key;
+            }
+            public void remove() {
+                removeAscending();
+            }
+            public Spliterator<K> trySplit() {
+                return null;
+            }
+            public void forEachRemaining(Consumer<? super K> action) {
+                while (hasNext())
+                    action.accept(next());
+            }
+            public boolean tryAdvance(Consumer<? super K> action) {
+                if (hasNext()) {
+                    action.accept(next());
+                    return true;
+                }
+                return false;
+            }
+            public long estimateSize() {
+                return Long.MAX_VALUE;
+            }
+            public int characteristics() {
+                return Spliterator.DISTINCT | Spliterator.ORDERED |
+                    Spliterator.SORTED;
+            }
+            public final Comparator<? super K>  getComparator() {
+                return NavigableSubMap.this.comparator();
+            }
+        }
+
+        final class DescendingSubMapKeyIterator extends SubMapIterator<K>
+            implements Spliterator<K> {
             DescendingSubMapKeyIterator(TreeMapEntry<K,V> last,
                                         TreeMapEntry<K,V> fence) {
                 super(last, fence);
@@ -1725,6 +1805,26 @@
             public void remove() {
                 removeDescending();
             }
+            public Spliterator<K> trySplit() {
+                return null;
+            }
+            public void forEachRemaining(Consumer<? super K> action) {
+                while (hasNext())
+                    action.accept(next());
+            }
+            public boolean tryAdvance(Consumer<? super K> action) {
+                if (hasNext()) {
+                    action.accept(next());
+                    return true;
+                }
+                return false;
+            }
+            public long estimateSize() {
+                return Long.MAX_VALUE;
+            }
+            public int characteristics() {
+                return Spliterator.DISTINCT | Spliterator.ORDERED;
+            }
         }
     }
 
@@ -1750,9 +1850,9 @@
                 throw new IllegalArgumentException("fromKey out of range");
             if (!inRange(toKey, toInclusive))
                 throw new IllegalArgumentException("toKey out of range");
-            return new AscendingSubMap(m,
-                                       false, fromKey, fromInclusive,
-                                       false, toKey,   toInclusive);
+            return new AscendingSubMap<>(m,
+                                         false, fromKey, fromInclusive,
+                                         false, toKey,   toInclusive);
         }
 
         public NavigableMap<K,V> headMap(K toKey, boolean inclusive) {
@@ -1763,9 +1863,9 @@
                 !hiInclusive && !inclusive))
             // ----- END android -----
                 throw new IllegalArgumentException("toKey out of range");
-            return new AscendingSubMap(m,
-                                       fromStart, lo,    loInclusive,
-                                       false,     toKey, inclusive);
+            return new AscendingSubMap<>(m,
+                                         fromStart, lo,    loInclusive,
+                                         false,     toKey, inclusive);
         }
 
         public NavigableMap<K,V> tailMap(K fromKey, boolean inclusive) {
@@ -1776,24 +1876,28 @@
                 !loInclusive && !inclusive))
             // ----- END android -----
                 throw new IllegalArgumentException("fromKey out of range");
-            return new AscendingSubMap(m,
-                                       false, fromKey, inclusive,
-                                       toEnd, hi,      hiInclusive);
+            return new AscendingSubMap<>(m,
+                                         false, fromKey, inclusive,
+                                         toEnd, hi,      hiInclusive);
         }
 
         public NavigableMap<K,V> descendingMap() {
             NavigableMap<K,V> mv = descendingMapView;
             return (mv != null) ? mv :
                 (descendingMapView =
-                 new DescendingSubMap(m,
-                                      fromStart, lo, loInclusive,
-                                      toEnd,     hi, hiInclusive));
+                 new DescendingSubMap<>(m,
+                                        fromStart, lo, loInclusive,
+                                        toEnd,     hi, hiInclusive));
         }
 
         Iterator<K> keyIterator() {
             return new SubMapKeyIterator(absLowest(), absHighFence());
         }
 
+        Spliterator<K> keySpliterator() {
+            return new SubMapKeyIterator(absLowest(), absHighFence());
+        }
+
         Iterator<K> descendingKeyIterator() {
             return new DescendingSubMapKeyIterator(absHighest(), absLowFence());
         }
@@ -1841,9 +1945,9 @@
                 throw new IllegalArgumentException("fromKey out of range");
             if (!inRange(toKey, toInclusive))
                 throw new IllegalArgumentException("toKey out of range");
-            return new DescendingSubMap(m,
-                                        false, toKey,   toInclusive,
-                                        false, fromKey, fromInclusive);
+            return new DescendingSubMap<>(m,
+                                          false, toKey,   toInclusive,
+                                          false, fromKey, fromInclusive);
         }
 
         public NavigableMap<K,V> headMap(K toKey, boolean inclusive) {
@@ -1854,9 +1958,9 @@
                 !loInclusive && !inclusive))
             // ----- END android -----
                 throw new IllegalArgumentException("toKey out of range");
-            return new DescendingSubMap(m,
-                                        false, toKey, inclusive,
-                                        toEnd, hi,    hiInclusive);
+            return new DescendingSubMap<>(m,
+                                          false, toKey, inclusive,
+                                          toEnd, hi,    hiInclusive);
         }
 
         public NavigableMap<K,V> tailMap(K fromKey, boolean inclusive) {
@@ -1867,24 +1971,28 @@
                 !hiInclusive && !inclusive))
             // ----- END android -----
                 throw new IllegalArgumentException("fromKey out of range");
-            return new DescendingSubMap(m,
-                                        fromStart, lo, loInclusive,
-                                        false, fromKey, inclusive);
+            return new DescendingSubMap<>(m,
+                                          fromStart, lo, loInclusive,
+                                          false, fromKey, inclusive);
         }
 
         public NavigableMap<K,V> descendingMap() {
             NavigableMap<K,V> mv = descendingMapView;
             return (mv != null) ? mv :
                 (descendingMapView =
-                 new AscendingSubMap(m,
-                                     fromStart, lo, loInclusive,
-                                     toEnd,     hi, hiInclusive));
+                 new AscendingSubMap<>(m,
+                                       fromStart, lo, loInclusive,
+                                       toEnd,     hi, hiInclusive));
         }
 
         Iterator<K> keyIterator() {
             return new DescendingSubMapKeyIterator(absHighest(), absLowFence());
         }
 
+        Spliterator<K> keySpliterator() {
+            return new DescendingSubMapKeyIterator(absHighest(), absLowFence());
+        }
+
         Iterator<K> descendingKeyIterator() {
             return new SubMapKeyIterator(absLowest(), absHighFence());
         }
@@ -1897,7 +2005,7 @@
 
         public Set<Map.Entry<K,V>> entrySet() {
             EntrySetView es = entrySetView;
-            return (es != null) ? es : new DescendingEntrySetView();
+            return (es != null) ? es : (entrySetView = new DescendingEntrySetView());
         }
 
         TreeMapEntry<K,V> subLowest()       { return absHighest(); }
@@ -1923,9 +2031,9 @@
         private boolean fromStart = false, toEnd = false;
         private K fromKey, toKey;
         private Object readResolve() {
-            return new AscendingSubMap(TreeMap.this,
-                                       fromStart, fromKey, true,
-                                       toEnd, toKey, false);
+            return new AscendingSubMap<>(TreeMap.this,
+                                         fromStart, fromKey, true,
+                                         toEnd, toKey, false);
         }
         public Set<Map.Entry<K,V>> entrySet() { throw new InternalError(); }
         public K lastKey() { throw new InternalError(); }
@@ -2392,12 +2500,12 @@
      * @param defaultVal if non-null, this default value is used for
      *        each value in the map.  If null, each value is read from
      *        iterator or stream, as described above.
-     * @throws IOException propagated from stream reads. This cannot
+     * @throws java.io.IOException propagated from stream reads. This cannot
      *         occur if str is null.
      * @throws ClassNotFoundException propagated from readObject.
      *         This cannot occur if str is null.
      */
-    private void buildFromSorted(int size, Iterator it,
+    private void buildFromSorted(int size, Iterator<?> it,
                                  java.io.ObjectInputStream str,
                                  V defaultVal)
         throws  java.io.IOException, ClassNotFoundException {
@@ -2420,9 +2528,10 @@
      * @param redLevel the level at which nodes should be red.
      *        Must be equal to computeRedLevel for tree of this size.
      */
+    @SuppressWarnings("unchecked")
     private final TreeMapEntry<K,V> buildFromSorted(int level, int lo, int hi,
                                              int redLevel,
-                                             Iterator it,
+                                             Iterator<?> it,
                                              java.io.ObjectInputStream str,
                                              V defaultVal)
         throws  java.io.IOException, ClassNotFoundException {
@@ -2500,4 +2609,416 @@
             level++;
         return level;
     }
+
+    /**
+     * Currently, we support Spliterator-based versions only for the
+     * full map, in either plain of descending form, otherwise relying
+     * on defaults because size estimation for submaps would dominate
+     * costs. The type tests needed to check these for key views are
+     * not very nice but avoid disrupting existing class
+     * structures. Callers must use plain default spliterators if this
+     * returns null.
+     */
+    static <K> Spliterator<K> keySpliteratorFor(NavigableMap<K,?> m) {
+        if (m instanceof TreeMap) {
+            @SuppressWarnings("unchecked") TreeMap<K,Object> t =
+                (TreeMap<K,Object>) m;
+            return t.keySpliterator();
+        }
+        if (m instanceof DescendingSubMap) {
+            @SuppressWarnings("unchecked") DescendingSubMap<K,?> dm =
+                (DescendingSubMap<K,?>) m;
+            TreeMap<K,?> tm = dm.m;
+            if (dm == tm.descendingMap) {
+                @SuppressWarnings("unchecked") TreeMap<K,Object> t =
+                    (TreeMap<K,Object>) tm;
+                return t.descendingKeySpliterator();
+            }
+        }
+        @SuppressWarnings("unchecked") NavigableSubMap<K,?> sm =
+            (NavigableSubMap<K,?>) m;
+        return sm.keySpliterator();
+    }
+
+    final Spliterator<K> keySpliterator() {
+        return new KeySpliterator<K,V>(this, null, null, 0, -1, 0);
+    }
+
+    final Spliterator<K> descendingKeySpliterator() {
+        return new DescendingKeySpliterator<K,V>(this, null, null, 0, -2, 0);
+    }
+
+    /**
+     * Base class for spliterators.  Iteration starts at a given
+     * origin and continues up to but not including a given fence (or
+     * null for end).  At top-level, for ascending cases, the first
+     * split uses the root as left-fence/right-origin. From there,
+     * right-hand splits replace the current fence with its left
+     * child, also serving as origin for the split-off spliterator.
+     * Left-hands are symmetric. Descending versions place the origin
+     * at the end and invert ascending split rules.  This base class
+     * is non-commital about directionality, or whether the top-level
+     * spliterator covers the whole tree. This means that the actual
+     * split mechanics are located in subclasses. Some of the subclass
+     * trySplit methods are identical (except for return types), but
+     * not nicely factorable.
+     *
+     * Currently, subclass versions exist only for the full map
+     * (including descending keys via its descendingMap).  Others are
+     * possible but currently not worthwhile because submaps require
+     * O(n) computations to determine size, which substantially limits
+     * potential speed-ups of using custom Spliterators versus default
+     * mechanics.
+     *
+     * To boostrap initialization, external constructors use
+     * negative size estimates: -1 for ascend, -2 for descend.
+     */
+    static class TreeMapSpliterator<K,V> {
+        final TreeMap<K,V> tree;
+        TreeMapEntry<K,V> current; // traverser; initially first node in range
+        TreeMapEntry<K,V> fence;   // one past last, or null
+        int side;                   // 0: top, -1: is a left split, +1: right
+        int est;                    // size estimate (exact only for top-level)
+        int expectedModCount;       // for CME checks
+
+        TreeMapSpliterator(TreeMap<K,V> tree,
+                           TreeMapEntry<K,V> origin, TreeMapEntry<K,V> fence,
+                           int side, int est, int expectedModCount) {
+            this.tree = tree;
+            this.current = origin;
+            this.fence = fence;
+            this.side = side;
+            this.est = est;
+            this.expectedModCount = expectedModCount;
+        }
+
+        final int getEstimate() { // force initialization
+            int s; TreeMap<K,V> t;
+            if ((s = est) < 0) {
+                if ((t = tree) != null) {
+                    current = (s == -1) ? t.getFirstEntry() : t.getLastEntry();
+                    s = est = t.size;
+                    expectedModCount = t.modCount;
+                }
+                else
+                    s = est = 0;
+            }
+            return s;
+        }
+
+        public final long estimateSize() {
+            return (long)getEstimate();
+        }
+    }
+
+    static final class KeySpliterator<K,V>
+        extends TreeMapSpliterator<K,V>
+        implements Spliterator<K> {
+        KeySpliterator(TreeMap<K,V> tree,
+                       TreeMapEntry<K,V> origin, TreeMapEntry<K,V> fence,
+                       int side, int est, int expectedModCount) {
+            super(tree, origin, fence, side, est, expectedModCount);
+        }
+
+        public KeySpliterator<K,V> trySplit() {
+            if (est < 0)
+                getEstimate(); // force initialization
+            int d = side;
+            TreeMapEntry<K,V> e = current, f = fence,
+                s = ((e == null || e == f) ? null :      // empty
+                     (d == 0)              ? tree.root : // was top
+                     (d >  0)              ? e.right :   // was right
+                     (d <  0 && f != null) ? f.left :    // was left
+                     null);
+            if (s != null && s != e && s != f &&
+                tree.compare(e.key, s.key) < 0) {        // e not already past s
+                side = 1;
+                return new KeySpliterator<>
+                    (tree, e, current = s, -1, est >>>= 1, expectedModCount);
+            }
+            return null;
+        }
+
+        public void forEachRemaining(Consumer<? super K> action) {
+            if (action == null)
+                throw new NullPointerException();
+            if (est < 0)
+                getEstimate(); // force initialization
+            TreeMapEntry<K,V> f = fence, e, p, pl;
+            if ((e = current) != null && e != f) {
+                current = f; // exhaust
+                do {
+                    action.accept(e.key);
+                    if ((p = e.right) != null) {
+                        while ((pl = p.left) != null)
+                            p = pl;
+                    }
+                    else {
+                        while ((p = e.parent) != null && e == p.right)
+                            e = p;
+                    }
+                } while ((e = p) != null && e != f);
+                if (tree.modCount != expectedModCount)
+                    throw new ConcurrentModificationException();
+            }
+        }
+
+        public boolean tryAdvance(Consumer<? super K> action) {
+            TreeMapEntry<K,V> e;
+            if (action == null)
+                throw new NullPointerException();
+            if (est < 0)
+                getEstimate(); // force initialization
+            if ((e = current) == null || e == fence)
+                return false;
+            current = successor(e);
+            action.accept(e.key);
+            if (tree.modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+            return true;
+        }
+
+        public int characteristics() {
+            return (side == 0 ? Spliterator.SIZED : 0) |
+                Spliterator.DISTINCT | Spliterator.SORTED | Spliterator.ORDERED;
+        }
+
+        public final Comparator<? super K>  getComparator() {
+            return tree.comparator;
+        }
+
+    }
+
+    static final class DescendingKeySpliterator<K,V>
+        extends TreeMapSpliterator<K,V>
+        implements Spliterator<K> {
+        DescendingKeySpliterator(TreeMap<K,V> tree,
+                                 TreeMapEntry<K,V> origin, TreeMapEntry<K,V> fence,
+                                 int side, int est, int expectedModCount) {
+            super(tree, origin, fence, side, est, expectedModCount);
+        }
+
+        public DescendingKeySpliterator<K,V> trySplit() {
+            if (est < 0)
+                getEstimate(); // force initialization
+            int d = side;
+            TreeMapEntry<K,V> e = current, f = fence,
+                    s = ((e == null || e == f) ? null :      // empty
+                         (d == 0)              ? tree.root : // was top
+                         (d <  0)              ? e.left :    // was left
+                         (d >  0 && f != null) ? f.right :   // was right
+                         null);
+            if (s != null && s != e && s != f &&
+                tree.compare(e.key, s.key) > 0) {       // e not already past s
+                side = 1;
+                return new DescendingKeySpliterator<>
+                        (tree, e, current = s, -1, est >>>= 1, expectedModCount);
+            }
+            return null;
+        }
+
+        public void forEachRemaining(Consumer<? super K> action) {
+            if (action == null)
+                throw new NullPointerException();
+            if (est < 0)
+                getEstimate(); // force initialization
+            TreeMapEntry<K,V> f = fence, e, p, pr;
+            if ((e = current) != null && e != f) {
+                current = f; // exhaust
+                do {
+                    action.accept(e.key);
+                    if ((p = e.left) != null) {
+                        while ((pr = p.right) != null)
+                            p = pr;
+                    }
+                    else {
+                        while ((p = e.parent) != null && e == p.left)
+                            e = p;
+                    }
+                } while ((e = p) != null && e != f);
+                if (tree.modCount != expectedModCount)
+                    throw new ConcurrentModificationException();
+            }
+        }
+
+        public boolean tryAdvance(Consumer<? super K> action) {
+            TreeMapEntry<K,V> e;
+            if (action == null)
+                throw new NullPointerException();
+            if (est < 0)
+                getEstimate(); // force initialization
+            if ((e = current) == null || e == fence)
+                return false;
+            current = predecessor(e);
+            action.accept(e.key);
+            if (tree.modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+            return true;
+        }
+
+        public int characteristics() {
+            return (side == 0 ? Spliterator.SIZED : 0) |
+                Spliterator.DISTINCT | Spliterator.ORDERED;
+        }
+    }
+
+    static final class ValueSpliterator<K,V>
+            extends TreeMapSpliterator<K,V>
+            implements Spliterator<V> {
+        ValueSpliterator(TreeMap<K,V> tree,
+                         TreeMapEntry<K,V> origin, TreeMapEntry<K,V> fence,
+                         int side, int est, int expectedModCount) {
+            super(tree, origin, fence, side, est, expectedModCount);
+        }
+
+        public ValueSpliterator<K,V> trySplit() {
+            if (est < 0)
+                getEstimate(); // force initialization
+            int d = side;
+            TreeMapEntry<K,V> e = current, f = fence,
+                    s = ((e == null || e == f) ? null :      // empty
+                         (d == 0)              ? tree.root : // was top
+                         (d >  0)              ? e.right :   // was right
+                         (d <  0 && f != null) ? f.left :    // was left
+                         null);
+            if (s != null && s != e && s != f &&
+                tree.compare(e.key, s.key) < 0) {        // e not already past s
+                side = 1;
+                return new ValueSpliterator<>
+                        (tree, e, current = s, -1, est >>>= 1, expectedModCount);
+            }
+            return null;
+        }
+
+        public void forEachRemaining(Consumer<? super V> action) {
+            if (action == null)
+                throw new NullPointerException();
+            if (est < 0)
+                getEstimate(); // force initialization
+            TreeMapEntry<K,V> f = fence, e, p, pl;
+            if ((e = current) != null && e != f) {
+                current = f; // exhaust
+                do {
+                    action.accept(e.value);
+                    if ((p = e.right) != null) {
+                        while ((pl = p.left) != null)
+                            p = pl;
+                    }
+                    else {
+                        while ((p = e.parent) != null && e == p.right)
+                            e = p;
+                    }
+                } while ((e = p) != null && e != f);
+                if (tree.modCount != expectedModCount)
+                    throw new ConcurrentModificationException();
+            }
+        }
+
+        public boolean tryAdvance(Consumer<? super V> action) {
+            TreeMapEntry<K,V> e;
+            if (action == null)
+                throw new NullPointerException();
+            if (est < 0)
+                getEstimate(); // force initialization
+            if ((e = current) == null || e == fence)
+                return false;
+            current = successor(e);
+            action.accept(e.value);
+            if (tree.modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+            return true;
+        }
+
+        public int characteristics() {
+            return (side == 0 ? Spliterator.SIZED : 0) | Spliterator.ORDERED;
+        }
+    }
+
+    static final class EntrySpliterator<K,V>
+        extends TreeMapSpliterator<K,V>
+        implements Spliterator<Map.Entry<K,V>> {
+        EntrySpliterator(TreeMap<K,V> tree,
+                         TreeMapEntry<K,V> origin, TreeMapEntry<K,V> fence,
+                         int side, int est, int expectedModCount) {
+            super(tree, origin, fence, side, est, expectedModCount);
+        }
+
+        public EntrySpliterator<K,V> trySplit() {
+            if (est < 0)
+                getEstimate(); // force initialization
+            int d = side;
+            TreeMapEntry<K,V> e = current, f = fence,
+                    s = ((e == null || e == f) ? null :      // empty
+                         (d == 0)              ? tree.root : // was top
+                         (d >  0)              ? e.right :   // was right
+                         (d <  0 && f != null) ? f.left :    // was left
+                         null);
+            if (s != null && s != e && s != f &&
+                tree.compare(e.key, s.key) < 0) {        // e not already past s
+                side = 1;
+                return new EntrySpliterator<>
+                        (tree, e, current = s, -1, est >>>= 1, expectedModCount);
+            }
+            return null;
+        }
+
+        public void forEachRemaining(Consumer<? super Map.Entry<K, V>> action) {
+            if (action == null)
+                throw new NullPointerException();
+            if (est < 0)
+                getEstimate(); // force initialization
+            TreeMapEntry<K,V> f = fence, e, p, pl;
+            if ((e = current) != null && e != f) {
+                current = f; // exhaust
+                do {
+                    action.accept(e);
+                    if ((p = e.right) != null) {
+                        while ((pl = p.left) != null)
+                            p = pl;
+                    }
+                    else {
+                        while ((p = e.parent) != null && e == p.right)
+                            e = p;
+                    }
+                } while ((e = p) != null && e != f);
+                if (tree.modCount != expectedModCount)
+                    throw new ConcurrentModificationException();
+            }
+        }
+
+        public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {
+            TreeMapEntry<K,V> e;
+            if (action == null)
+                throw new NullPointerException();
+            if (est < 0)
+                getEstimate(); // force initialization
+            if ((e = current) == null || e == fence)
+                return false;
+            current = successor(e);
+            action.accept(e);
+            if (tree.modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+            return true;
+        }
+
+        public int characteristics() {
+            return (side == 0 ? Spliterator.SIZED : 0) |
+                    Spliterator.DISTINCT | Spliterator.SORTED | Spliterator.ORDERED;
+        }
+
+        @Override
+        public Comparator<Map.Entry<K, V>> getComparator() {
+            // Adapt or create a key-based comparator
+            if (tree.comparator != null) {
+                return Map.Entry.comparingByKey(tree.comparator);
+            }
+            else {
+                return (Comparator<Map.Entry<K, V>> & Serializable) (e1, e2) -> {
+                    @SuppressWarnings("unchecked")
+                    Comparable<? super K> k1 = (Comparable<? super K>) e1.getKey();
+                    return k1.compareTo(e2.getKey());
+                };
+            }
+        }
+    }
 }
diff --git a/ojluni/src/main/java/java/util/TreeSet.java b/ojluni/src/main/java/java/util/TreeSet.java
index 3eee2a8..54eeeda 100755
--- a/ojluni/src/main/java/java/util/TreeSet.java
+++ b/ojluni/src/main/java/java/util/TreeSet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -302,7 +302,7 @@
             m instanceof TreeMap) {
             SortedSet<? extends E> set = (SortedSet<? extends E>) c;
             TreeMap<E,Object> map = (TreeMap<E, Object>) m;
-            Comparator<? super E> cc = (Comparator<? super E>) set.comparator();
+            Comparator<?> cc = set.comparator();
             Comparator<? super E> mc = map.comparator();
             if (cc==mc || (cc != null && cc.equals(mc))) {
                 map.addAllForTreeSet(set, PRESENT);
@@ -469,12 +469,13 @@
      *
      * @return a shallow copy of this set
      */
+    @SuppressWarnings("unchecked")
     public Object clone() {
-        TreeSet<E> clone = null;
+        TreeSet<E> clone;
         try {
             clone = (TreeSet<E>) super.clone();
         } catch (CloneNotSupportedException e) {
-            throw new InternalError();
+            throw new InternalError(e);
         }
 
         clone.m = new TreeMap<>(m);
@@ -519,14 +520,11 @@
         s.defaultReadObject();
 
         // Read in Comparator
-        Comparator<? super E> c = (Comparator<? super E>) s.readObject();
+        @SuppressWarnings("unchecked")
+            Comparator<? super E> c = (Comparator<? super E>) s.readObject();
 
         // Create backing TreeMap
-        TreeMap<E,Object> tm;
-        if (c==null)
-            tm = new TreeMap<>();
-        else
-            tm = new TreeMap<>(c);
+        TreeMap<E,Object> tm = new TreeMap<>(c);
         m = tm;
 
         // Read in size
@@ -535,5 +533,28 @@
         tm.readTreeSet(size, s, PRESENT);
     }
 
+    /**
+     * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
+     * and <em>fail-fast</em> {@link Spliterator} over the elements in this
+     * set.
+     *
+     * <p>The {@code Spliterator} reports {@link Spliterator#SIZED},
+     * {@link Spliterator#DISTINCT}, {@link Spliterator#SORTED}, and
+     * {@link Spliterator#ORDERED}.  Overriding implementations should document
+     * the reporting of additional characteristic values.
+     *
+     * <p>The spliterator's comparator (see
+     * {@link java.util.Spliterator#getComparator()}) is {@code null} if
+     * the tree set's comparator (see {@link #comparator()}) is {@code null}.
+     * Otherwise, the spliterator's comparator is the same as or imposes the
+     * same total ordering as the tree set's comparator.
+     *
+     * @return a {@code Spliterator} over the elements in this set
+     * @since 1.8
+     */
+    public Spliterator<E> spliterator() {
+        return TreeMap.keySpliteratorFor(m);
+    }
+
     private static final long serialVersionUID = -2479143000061671589L;
 }
diff --git a/ojluni/src/main/java/java/util/Vector.java b/ojluni/src/main/java/java/util/Vector.java
index 6eedfe9..f43326f 100755
--- a/ojluni/src/main/java/java/util/Vector.java
+++ b/ojluni/src/main/java/java/util/Vector.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -43,9 +43,9 @@
  * capacity of a vector before inserting a large number of
  * components; this reduces the amount of incremental reallocation.
  *
- * <p><a name="fail-fast"/>
+ * <p><a name="fail-fast">
  * The iterators returned by this class's {@link #iterator() iterator} and
- * {@link #listIterator(int) listIterator} methods are <em>fail-fast</em>:
+ * {@link #listIterator(int) listIterator} methods are <em>fail-fast</em></a>:
  * if the vector is structurally modified at any time after the iterator is
  * created, in any way except through the iterator's own
  * {@link ListIterator#remove() remove} or
@@ -675,7 +675,7 @@
             return v;
         } catch (CloneNotSupportedException e) {
             // this shouldn't happen, since we are Cloneable
-            throw new InternalError();
+            throw new InternalError(e);
         }
     }
 
@@ -1250,4 +1250,109 @@
             throw new ConcurrentModificationException();
         }
     }
+
+ /**
+     * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
+     * and <em>fail-fast</em> {@link Spliterator} over the elements in this
+     * list.
+     *
+     * <p>The {@code Spliterator} reports {@link Spliterator#SIZED},
+     * {@link Spliterator#SUBSIZED}, and {@link Spliterator#ORDERED}.
+     * Overriding implementations should document the reporting of additional
+     * characteristic values.
+     *
+     * @return a {@code Spliterator} over the elements in this list
+     * @since 1.8
+     */
+    @Override
+    public Spliterator<E> spliterator() {
+        return new VectorSpliterator<>(this, null, 0, -1, 0);
+    }
+
+    /** Similar to ArrayList Spliterator */
+    static final class VectorSpliterator<E> implements Spliterator<E> {
+        private final Vector<E> list;
+        private Object[] array;
+        private int index; // current index, modified on advance/split
+        private int fence; // -1 until used; then one past last index
+        private int expectedModCount; // initialized when fence set
+
+        /** Create new spliterator covering the given  range */
+        VectorSpliterator(Vector<E> list, Object[] array, int origin, int fence,
+                          int expectedModCount) {
+            this.list = list;
+            this.array = array;
+            this.index = origin;
+            this.fence = fence;
+            this.expectedModCount = expectedModCount;
+        }
+
+        private int getFence() { // initialize on first use
+            int hi;
+            if ((hi = fence) < 0) {
+                synchronized(list) {
+                    array = list.elementData;
+                    expectedModCount = list.modCount;
+                    hi = fence = list.elementCount;
+                }
+            }
+            return hi;
+        }
+
+        public Spliterator<E> trySplit() {
+            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
+            return (lo >= mid) ? null :
+                new VectorSpliterator<E>(list, array, lo, index = mid,
+                                         expectedModCount);
+        }
+
+        @SuppressWarnings("unchecked")
+        public boolean tryAdvance(Consumer<? super E> action) {
+            int i;
+            if (action == null)
+                throw new NullPointerException();
+            if (getFence() > (i = index)) {
+                index = i + 1;
+                action.accept((E)array[i]);
+                if (list.modCount != expectedModCount)
+                    throw new ConcurrentModificationException();
+                return true;
+            }
+            return false;
+        }
+
+        @SuppressWarnings("unchecked")
+        public void forEachRemaining(Consumer<? super E> action) {
+            int i, hi; // hoist accesses and checks from loop
+            Vector<E> lst; Object[] a;
+            if (action == null)
+                throw new NullPointerException();
+            if ((lst = list) != null) {
+                if ((hi = fence) < 0) {
+                    synchronized(lst) {
+                        expectedModCount = lst.modCount;
+                        a = array = lst.elementData;
+                        hi = fence = lst.elementCount;
+                    }
+                }
+                else
+                    a = array;
+                if (a != null && (i = index) >= 0 && (index = hi) <= a.length) {
+                    while (i < hi)
+                        action.accept((E) a[i++]);
+                    if (lst.modCount == expectedModCount)
+                        return;
+                }
+            }
+            throw new ConcurrentModificationException();
+        }
+
+        public long estimateSize() {
+            return (long) (getFence() - index);
+        }
+
+        public int characteristics() {
+            return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
+        }
+    }
 }
diff --git a/ojluni/src/main/java/java/util/WeakHashMap.java b/ojluni/src/main/java/java/util/WeakHashMap.java
index fa5dace..9edf7b7 100755
--- a/ojluni/src/main/java/java/util/WeakHashMap.java
+++ b/ojluni/src/main/java/java/util/WeakHashMap.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -982,6 +982,10 @@
         public void clear() {
             WeakHashMap.this.clear();
         }
+
+        public Spliterator<K> spliterator() {
+            return new KeySpliterator<>(WeakHashMap.this, 0, -1, 0, 0);
+        }
     }
 
     /**
@@ -1018,6 +1022,10 @@
         public void clear() {
             WeakHashMap.this.clear();
         }
+
+        public Spliterator<V> spliterator() {
+            return new ValueSpliterator<>(WeakHashMap.this, 0, -1, 0, 0);
+        }
     }
 
     /**
@@ -1078,6 +1086,10 @@
         public <T> T[] toArray(T[] a) {
             return deepCopy().toArray(a);
         }
+
+        public Spliterator<Map.Entry<K,V>> spliterator() {
+            return new EntrySpliterator<>(WeakHashMap.this, 0, -1, 0, 0);
+        }
     }
 
     @SuppressWarnings("unchecked")
@@ -1101,4 +1113,286 @@
             }
         }
     }
+
+    /**
+     * Similar form as other hash Spliterators, but skips dead
+     * elements.
+     */
+    static class WeakHashMapSpliterator<K,V> {
+        final WeakHashMap<K,V> map;
+        WeakHashMap.Entry<K,V> current; // current node
+        int index;             // current index, modified on advance/split
+        int fence;             // -1 until first use; then one past last index
+        int est;               // size estimate
+        int expectedModCount;  // for comodification checks
+
+        WeakHashMapSpliterator(WeakHashMap<K,V> m, int origin,
+                               int fence, int est,
+                               int expectedModCount) {
+            this.map = m;
+            this.index = origin;
+            this.fence = fence;
+            this.est = est;
+            this.expectedModCount = expectedModCount;
+        }
+
+        final int getFence() { // initialize fence and size on first use
+            int hi;
+            if ((hi = fence) < 0) {
+                WeakHashMap<K,V> m = map;
+                est = m.size();
+                expectedModCount = m.modCount;
+                hi = fence = m.table.length;
+            }
+            return hi;
+        }
+
+        public final long estimateSize() {
+            getFence(); // force init
+            return (long) est;
+        }
+    }
+
+    static final class KeySpliterator<K,V>
+        extends WeakHashMapSpliterator<K,V>
+        implements Spliterator<K> {
+        KeySpliterator(WeakHashMap<K,V> m, int origin, int fence, int est,
+                       int expectedModCount) {
+            super(m, origin, fence, est, expectedModCount);
+        }
+
+        public KeySpliterator<K,V> trySplit() {
+            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
+            return (lo >= mid) ? null :
+                new KeySpliterator<K,V>(map, lo, index = mid, est >>>= 1,
+                                        expectedModCount);
+        }
+
+        public void forEachRemaining(Consumer<? super K> action) {
+            int i, hi, mc;
+            if (action == null)
+                throw new NullPointerException();
+            WeakHashMap<K,V> m = map;
+            WeakHashMap.Entry<K,V>[] tab = m.table;
+            if ((hi = fence) < 0) {
+                mc = expectedModCount = m.modCount;
+                hi = fence = tab.length;
+            }
+            else
+                mc = expectedModCount;
+            if (tab.length >= hi && (i = index) >= 0 &&
+                (i < (index = hi) || current != null)) {
+                WeakHashMap.Entry<K,V> p = current;
+                current = null; // exhaust
+                do {
+                    if (p == null)
+                        p = tab[i++];
+                    else {
+                        Object x = p.get();
+                        p = p.next;
+                        if (x != null) {
+                            @SuppressWarnings("unchecked") K k =
+                                (K) WeakHashMap.unmaskNull(x);
+                            action.accept(k);
+                        }
+                    }
+                } while (p != null || i < hi);
+            }
+            if (m.modCount != mc)
+                throw new ConcurrentModificationException();
+        }
+
+        public boolean tryAdvance(Consumer<? super K> action) {
+            int hi;
+            if (action == null)
+                throw new NullPointerException();
+            WeakHashMap.Entry<K,V>[] tab = map.table;
+            if (tab.length >= (hi = getFence()) && index >= 0) {
+                while (current != null || index < hi) {
+                    if (current == null)
+                        current = tab[index++];
+                    else {
+                        Object x = current.get();
+                        current = current.next;
+                        if (x != null) {
+                            @SuppressWarnings("unchecked") K k =
+                                (K) WeakHashMap.unmaskNull(x);
+                            action.accept(k);
+                            if (map.modCount != expectedModCount)
+                                throw new ConcurrentModificationException();
+                            return true;
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+
+        public int characteristics() {
+            return Spliterator.DISTINCT;
+        }
+    }
+
+    static final class ValueSpliterator<K,V>
+        extends WeakHashMapSpliterator<K,V>
+        implements Spliterator<V> {
+        ValueSpliterator(WeakHashMap<K,V> m, int origin, int fence, int est,
+                         int expectedModCount) {
+            super(m, origin, fence, est, expectedModCount);
+        }
+
+        public ValueSpliterator<K,V> trySplit() {
+            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
+            return (lo >= mid) ? null :
+                new ValueSpliterator<K,V>(map, lo, index = mid, est >>>= 1,
+                                          expectedModCount);
+        }
+
+        public void forEachRemaining(Consumer<? super V> action) {
+            int i, hi, mc;
+            if (action == null)
+                throw new NullPointerException();
+            WeakHashMap<K,V> m = map;
+            WeakHashMap.Entry<K,V>[] tab = m.table;
+            if ((hi = fence) < 0) {
+                mc = expectedModCount = m.modCount;
+                hi = fence = tab.length;
+            }
+            else
+                mc = expectedModCount;
+            if (tab.length >= hi && (i = index) >= 0 &&
+                (i < (index = hi) || current != null)) {
+                WeakHashMap.Entry<K,V> p = current;
+                current = null; // exhaust
+                do {
+                    if (p == null)
+                        p = tab[i++];
+                    else {
+                        Object x = p.get();
+                        V v = p.value;
+                        p = p.next;
+                        if (x != null)
+                            action.accept(v);
+                    }
+                } while (p != null || i < hi);
+            }
+            if (m.modCount != mc)
+                throw new ConcurrentModificationException();
+        }
+
+        public boolean tryAdvance(Consumer<? super V> action) {
+            int hi;
+            if (action == null)
+                throw new NullPointerException();
+            WeakHashMap.Entry<K,V>[] tab = map.table;
+            if (tab.length >= (hi = getFence()) && index >= 0) {
+                while (current != null || index < hi) {
+                    if (current == null)
+                        current = tab[index++];
+                    else {
+                        Object x = current.get();
+                        V v = current.value;
+                        current = current.next;
+                        if (x != null) {
+                            action.accept(v);
+                            if (map.modCount != expectedModCount)
+                                throw new ConcurrentModificationException();
+                            return true;
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+
+        public int characteristics() {
+            return 0;
+        }
+    }
+
+    static final class EntrySpliterator<K,V>
+        extends WeakHashMapSpliterator<K,V>
+        implements Spliterator<Map.Entry<K,V>> {
+        EntrySpliterator(WeakHashMap<K,V> m, int origin, int fence, int est,
+                       int expectedModCount) {
+            super(m, origin, fence, est, expectedModCount);
+        }
+
+        public EntrySpliterator<K,V> trySplit() {
+            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
+            return (lo >= mid) ? null :
+                new EntrySpliterator<K,V>(map, lo, index = mid, est >>>= 1,
+                                          expectedModCount);
+        }
+
+
+        public void forEachRemaining(Consumer<? super Map.Entry<K, V>> action) {
+            int i, hi, mc;
+            if (action == null)
+                throw new NullPointerException();
+            WeakHashMap<K,V> m = map;
+            WeakHashMap.Entry<K,V>[] tab = m.table;
+            if ((hi = fence) < 0) {
+                mc = expectedModCount = m.modCount;
+                hi = fence = tab.length;
+            }
+            else
+                mc = expectedModCount;
+            if (tab.length >= hi && (i = index) >= 0 &&
+                (i < (index = hi) || current != null)) {
+                WeakHashMap.Entry<K,V> p = current;
+                current = null; // exhaust
+                do {
+                    if (p == null)
+                        p = tab[i++];
+                    else {
+                        Object x = p.get();
+                        V v = p.value;
+                        p = p.next;
+                        if (x != null) {
+                            @SuppressWarnings("unchecked") K k =
+                                (K) WeakHashMap.unmaskNull(x);
+                            action.accept
+                                (new AbstractMap.SimpleImmutableEntry<K,V>(k, v));
+                        }
+                    }
+                } while (p != null || i < hi);
+            }
+            if (m.modCount != mc)
+                throw new ConcurrentModificationException();
+        }
+
+        public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {
+            int hi;
+            if (action == null)
+                throw new NullPointerException();
+            WeakHashMap.Entry<K,V>[] tab = map.table;
+            if (tab.length >= (hi = getFence()) && index >= 0) {
+                while (current != null || index < hi) {
+                    if (current == null)
+                        current = tab[index++];
+                    else {
+                        Object x = current.get();
+                        V v = current.value;
+                        current = current.next;
+                        if (x != null) {
+                            @SuppressWarnings("unchecked") K k =
+                                (K) WeakHashMap.unmaskNull(x);
+                            action.accept
+                                (new AbstractMap.SimpleImmutableEntry<K,V>(k, v));
+                            if (map.modCount != expectedModCount)
+                                throw new ConcurrentModificationException();
+                            return true;
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+
+        public int characteristics() {
+            return Spliterator.DISTINCT;
+        }
+    }
+
 }
diff --git a/openjdk_java_files.mk b/openjdk_java_files.mk
index 1863d51..03a87bf 100644
--- a/openjdk_java_files.mk
+++ b/openjdk_java_files.mk
@@ -817,6 +817,8 @@
     ojluni/src/main/java/java/util/SimpleTimeZone.java \
     ojluni/src/main/java/java/util/SortedMap.java \
     ojluni/src/main/java/java/util/SortedSet.java \
+    ojluni/src/main/java/java/util/Spliterator.java \
+    ojluni/src/main/java/java/util/Spliterators.java \
     ojluni/src/main/java/java/util/Stack.java \
     ojluni/src/main/java/java/util/StringTokenizer.java \
     ojluni/src/main/java/java/util/Timer.java \