8012645: Stream methods on BitSet, Random, ThreadLocalRandom, ZipFile
authormduigou
Thu, 02 May 2013 09:18:56 -0700
changeset 17421 f3fbcfe6e2cf
parent 17420 6163a8236046
child 17422 c04d73570793
8012645: Stream methods on BitSet, Random, ThreadLocalRandom, ZipFile Reviewed-by: mduigou, henryjen, alanb, martin, psandoz Contributed-by: akhil.arora@oracle.com, brian.goetz@oracle.com
jdk/src/share/classes/java/util/BitSet.java
jdk/src/share/classes/java/util/Random.java
jdk/src/share/classes/java/util/concurrent/ThreadLocalRandom.java
jdk/src/share/classes/java/util/jar/JarFile.java
jdk/src/share/classes/java/util/zip/ZipFile.java
jdk/test/java/util/BitSet/BitSetStreamTest.java
jdk/test/java/util/Random/RandomStreamTest.java
jdk/test/java/util/zip/ZipFile/StreamZipEntriesTest.java
--- a/jdk/src/share/classes/java/util/BitSet.java	Wed May 01 15:08:31 2013 -0700
+++ b/jdk/src/share/classes/java/util/BitSet.java	Thu May 02 09:18:56 2013 -0700
@@ -29,6 +29,8 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.LongBuffer;
+import java.util.stream.IntStream;
+import java.util.stream.StreamSupport;
 
 /**
  * This class implements a vector of bits that grows as needed. Each
@@ -1188,4 +1190,47 @@
         b.append('}');
         return b.toString();
     }
+
+    /**
+     * Returns a stream of indices for which this {@code BitSet}
+     * contains a bit in the set state. The indices are returned
+     * in order, from lowest to highest. The size of the stream
+     * is the number of bits in the set state, equal to the value
+     * returned by the {@link #cardinality()} method.
+     *
+     * <p>The bit set must remain constant during the execution of the
+     * terminal stream operation.  Otherwise, the result of the terminal
+     * stream operation is undefined.
+     *
+     * @return a stream of integers representing set indices
+     * @since 1.8
+     */
+    public IntStream stream() {
+        class BitSetIterator implements PrimitiveIterator.OfInt {
+            int next = nextSetBit(0);
+
+            @Override
+            public boolean hasNext() {
+                return next != -1;
+            }
+
+            @Override
+            public int nextInt() {
+                if (next != -1) {
+                    int ret = next;
+                    next = nextSetBit(next+1);
+                    return ret;
+                } else {
+                    throw new NoSuchElementException();
+                }
+            }
+        }
+
+        return StreamSupport.intStream(
+                () -> Spliterators.spliterator(
+                        new BitSetIterator(), cardinality(),
+                        Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.SORTED),
+                Spliterator.SIZED | Spliterator.SUBSIZED |
+                        Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.SORTED);
+    }
 }
--- a/jdk/src/share/classes/java/util/Random.java	Wed May 01 15:08:31 2013 -0700
+++ b/jdk/src/share/classes/java/util/Random.java	Thu May 02 09:18:56 2013 -0700
@@ -26,6 +26,10 @@
 package java.util;
 import java.io.*;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.DoubleStream;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
+
 import sun.misc.Unsafe;
 
 /**
@@ -513,6 +517,59 @@
     }
 
     /**
+     * Returns a stream of pseudorandom, uniformly distributed
+     * {@code integer} values from this random number generator's
+     * sequence. Values are obtained as needed by calling
+     * {@link #nextInt()}.
+     *
+     * @return an infinite stream of {@code integer} values
+     * @since 1.8
+     */
+    public IntStream ints() {
+        return IntStream.generate(this::nextInt);
+    }
+
+    /**
+     * Returns a stream of pseudorandom, uniformly distributed
+     * {@code long} values from this random number generator's
+     * sequence. Values are obtained as needed by calling
+     * {@link #nextLong()}.
+     *
+     * @return an infinite stream of {@code long} values
+     * @since 1.8
+     */
+    public LongStream longs() {
+        return LongStream.generate(this::nextLong);
+    }
+
+    /**
+     * Returns a stream of pseudorandom, uniformly distributed
+     * {@code double} values between {@code 0.0} and {@code 1.0}
+     * from this random number generator's sequence. Values are
+     * obtained as needed by calling {@link #nextDouble()}.
+     *
+     * @return an infinite stream of {@code double} values
+     * @since 1.8
+     */
+    public DoubleStream doubles() {
+        return DoubleStream.generate(this::nextDouble);
+    }
+
+    /**
+     * Returns a stream of pseudorandom, Gaussian ("normally")
+     * distributed {@code double} values with mean {@code 0.0}
+     * and standard deviation {@code 1.0} from this random number
+     * generator's sequence. Values are obtained as needed by
+     * calling {@link #nextGaussian()}.
+     *
+     * @return an infinite stream of {@code double} values
+     * @since 1.8
+     */
+    public DoubleStream gaussians() {
+        return DoubleStream.generate(this::nextGaussian);
+    }
+
+    /**
      * Serializable fields for Random.
      *
      * @serialField    seed long
--- a/jdk/src/share/classes/java/util/concurrent/ThreadLocalRandom.java	Wed May 01 15:08:31 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/ThreadLocalRandom.java	Thu May 02 09:18:56 2013 -0700
@@ -39,6 +39,9 @@
 import java.util.Random;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.DoubleStream;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
 
 /**
  * A random number generator isolated to the current thread.  Like the
@@ -241,6 +244,26 @@
         return offset + nextInt((int) n);
     }
 
+    @Override
+    public IntStream ints() {
+        return IntStream.generate(() -> current().nextInt());
+    }
+
+    @Override
+    public LongStream longs() {
+        return LongStream.generate(() -> current().nextLong());
+    }
+
+    @Override
+    public DoubleStream doubles() {
+        return DoubleStream.generate(() -> current().nextDouble());
+    }
+
+    @Override
+    public DoubleStream gaussians() {
+        return DoubleStream.generate(() -> current().nextGaussian());
+    }
+
     /**
      * Returns a pseudorandom, uniformly distributed value between the
      * given least value (inclusive) and bound (exclusive).
--- a/jdk/src/share/classes/java/util/jar/JarFile.java	Wed May 01 15:08:31 2013 -0700
+++ b/jdk/src/share/classes/java/util/jar/JarFile.java	Thu May 02 09:18:56 2013 -0700
@@ -29,6 +29,8 @@
 import java.lang.ref.SoftReference;
 import java.net.URL;
 import java.util.*;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 import java.util.zip.*;
 import java.security.CodeSigner;
 import java.security.cert.Certificate;
@@ -235,20 +237,42 @@
         return null;
     }
 
+    private class JarEntryIterator implements Enumeration<JarEntry>,
+            Iterator<JarEntry>
+    {
+        final Enumeration<? extends ZipEntry> e = JarFile.super.entries();
+
+        public boolean hasNext() {
+            return e.hasMoreElements();
+        }
+
+        public JarEntry next() {
+            ZipEntry ze = e.nextElement();
+            return new JarFileEntry(ze);
+        }
+
+        public boolean hasMoreElements() {
+            return hasNext();
+        }
+
+        public JarEntry nextElement() {
+            return next();
+        }
+    }
+
     /**
      * Returns an enumeration of the zip file entries.
      */
     public Enumeration<JarEntry> entries() {
-        final Enumeration<? extends ZipEntry> enum_ = super.entries();
-        return new Enumeration<JarEntry>() {
-            public boolean hasMoreElements() {
-                return enum_.hasMoreElements();
-            }
-            public JarFileEntry nextElement() {
-                ZipEntry ze = enum_.nextElement();
-                return new JarFileEntry(ze);
-            }
-        };
+        return new JarEntryIterator();
+    }
+
+    @Override
+    public Stream<JarEntry> stream() {
+        return StreamSupport.stream(Spliterators.spliterator(
+                new JarEntryIterator(), size(),
+                Spliterator.ORDERED | Spliterator.DISTINCT |
+                        Spliterator.IMMUTABLE | Spliterator.NONNULL));
     }
 
     private class JarFileEntry extends JarEntry {
--- a/jdk/src/share/classes/java/util/zip/ZipFile.java	Wed May 01 15:08:31 2013 -0700
+++ b/jdk/src/share/classes/java/util/zip/ZipFile.java	Thu May 02 09:18:56 2013 -0700
@@ -36,11 +36,15 @@
 import java.util.Deque;
 import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Spliterator;
+import java.util.Spliterators;
 import java.util.WeakHashMap;
-import java.security.AccessController;
-import sun.security.action.GetPropertyAction;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
 import static java.util.zip.ZipConstants64.*;
 
 /**
@@ -471,49 +475,80 @@
         return name;
     }
 
+    private class ZipEntryIterator implements Enumeration<ZipEntry>, Iterator<ZipEntry> {
+        private int i = 0;
+
+        public ZipEntryIterator() {
+            ensureOpen();
+        }
+
+        public boolean hasMoreElements() {
+            return hasNext();
+        }
+
+        public boolean hasNext() {
+            synchronized (ZipFile.this) {
+                ensureOpen();
+                return i < total;
+            }
+        }
+
+        public ZipEntry nextElement() {
+            return next();
+        }
+
+        public ZipEntry next() {
+            synchronized (ZipFile.this) {
+                ensureOpen();
+                if (i >= total) {
+                    throw new NoSuchElementException();
+                }
+                long jzentry = getNextEntry(jzfile, i++);
+                if (jzentry == 0) {
+                    String message;
+                    if (closeRequested) {
+                        message = "ZipFile concurrently closed";
+                    } else {
+                        message = getZipMessage(ZipFile.this.jzfile);
+                    }
+                    throw new ZipError("jzentry == 0" +
+                                       ",\n jzfile = " + ZipFile.this.jzfile +
+                                       ",\n total = " + ZipFile.this.total +
+                                       ",\n name = " + ZipFile.this.name +
+                                       ",\n i = " + i +
+                                       ",\n message = " + message
+                        );
+                }
+                ZipEntry ze = getZipEntry(null, jzentry);
+                freeEntry(jzfile, jzentry);
+                return ze;
+            }
+        }
+    }
+
     /**
      * Returns an enumeration of the ZIP file entries.
      * @return an enumeration of the ZIP file entries
      * @throws IllegalStateException if the zip file has been closed
      */
     public Enumeration<? extends ZipEntry> entries() {
-        ensureOpen();
-        return new Enumeration<ZipEntry>() {
-                private int i = 0;
-                public boolean hasMoreElements() {
-                    synchronized (ZipFile.this) {
-                        ensureOpen();
-                        return i < total;
-                    }
-                }
-                public ZipEntry nextElement() throws NoSuchElementException {
-                    synchronized (ZipFile.this) {
-                        ensureOpen();
-                        if (i >= total) {
-                            throw new NoSuchElementException();
-                        }
-                        long jzentry = getNextEntry(jzfile, i++);
-                        if (jzentry == 0) {
-                            String message;
-                            if (closeRequested) {
-                                message = "ZipFile concurrently closed";
-                            } else {
-                                message = getZipMessage(ZipFile.this.jzfile);
-                            }
-                            throw new ZipError("jzentry == 0" +
-                                               ",\n jzfile = " + ZipFile.this.jzfile +
-                                               ",\n total = " + ZipFile.this.total +
-                                               ",\n name = " + ZipFile.this.name +
-                                               ",\n i = " + i +
-                                               ",\n message = " + message
-                                );
-                        }
-                        ZipEntry ze = getZipEntry(null, jzentry);
-                        freeEntry(jzfile, jzentry);
-                        return ze;
-                    }
-                }
-            };
+        return new ZipEntryIterator();
+    }
+
+    /**
+     * Return an ordered {@code Stream} over the ZIP file entries.
+     * Entries appear in the {@code Stream} in the order they appear in
+     * the central directory of the ZIP file.
+     *
+     * @return an ordered {@code Stream} of entries in this ZIP file
+     * @throws IllegalStateException if the zip file has been closed
+     * @since 1.8
+     */
+    public Stream<? extends ZipEntry> stream() {
+        return StreamSupport.stream(Spliterators.spliterator(
+                new ZipEntryIterator(), size(),
+                Spliterator.ORDERED | Spliterator.DISTINCT |
+                        Spliterator.IMMUTABLE | Spliterator.NONNULL));
     }
 
     private ZipEntry getZipEntry(String name, long jzentry) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/BitSet/BitSetStreamTest.java	Thu May 02 09:18:56 2013 -0700
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ * 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.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.lang.Integer;
+import java.lang.Object;
+import java.lang.System;
+import java.util.BitSet;
+import java.util.OptionalInt;
+import java.util.PrimitiveIterator;
+import java.util.Random;
+import java.util.function.IntSupplier;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+/**
+ * @test
+ * @summary test BitSet stream
+ * @bug 8012645
+ * @run testng BitSetStreamTest
+ */
+public class BitSetStreamTest {
+    static class Fibs implements IntSupplier {
+        private int n1 = 0;
+        private int n2 = 1;
+
+        static int fibs(int n) {
+            Fibs f = new Fibs();
+            while (n-- > 0) f.getAsInt();
+            return f.getAsInt();
+        }
+
+        public int getAsInt() { int s = n1; n1 = n2; n2 = s + n1; return s; }
+    }
+
+    private static final Object[][] testcases = new Object[][] {
+        { "none", IntStream.empty() },
+        { "index 0", IntStream.of(0) },
+        { "index 255", IntStream.of(255) },
+        { "every bit", IntStream.range(0, 255) },
+        { "step 2", IntStream.range(0, 255, 2) },
+        { "step 3", IntStream.range(0, 255, 3) },
+        { "step 5", IntStream.range(0, 255, 5) },
+        { "step 7", IntStream.range(0, 255, 7) },
+        { "1, 10, 100, 1000", IntStream.of(1, 10, 100, 1000) },
+        { "25 fibs", IntStream.generate(new Fibs()).limit(25) }
+    };
+
+    @DataProvider(name = "cases")
+    public static Object[][] produceCases() {
+        return testcases;
+    }
+
+    @Test
+    public void testFibs() {
+        Fibs f = new Fibs();
+        assertEquals(0, f.getAsInt());
+        assertEquals(1, f.getAsInt());
+        assertEquals(1, f.getAsInt());
+        assertEquals(2, f.getAsInt());
+        assertEquals(3, f.getAsInt());
+        assertEquals(5, f.getAsInt());
+        assertEquals(8, f.getAsInt());
+        assertEquals(13, f.getAsInt());
+        assertEquals(987, Fibs.fibs(16));
+    }
+
+    @Test(dataProvider = "cases")
+    public void testBitsetStream(String name, IntStream data) {
+        BitSet bs = new BitSet();
+        long setBits = data.distinct()
+                           .peek(i -> bs.set(i))
+                           .count();
+
+        assertEquals(bs.cardinality(), setBits);
+        assertEquals(bs.cardinality(), bs.stream().reduce(0, (s, i) -> s+1));
+
+        PrimitiveIterator.OfInt it = bs.stream().iterator();
+        for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) {
+            assertTrue(it.hasNext());
+            assertEquals(it.nextInt(), i);
+        }
+        assertFalse(it.hasNext());
+    }
+
+    @Test
+    public void testRandomStream() {
+        final int size = 1024 * 1024;
+        final int[] seeds = {
+                2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
+                43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97};
+        final byte[] bytes = new byte[size];
+        for (int seed : seeds) {
+            final Random random = new Random(seed);
+            random.nextBytes(bytes);
+            final BitSet bitSet = BitSet.valueOf(bytes);
+            final int cardinality = bitSet.cardinality();
+            final IntStream stream = bitSet.stream();
+            final int[] array = stream.toArray();
+            assertEquals(array.length, cardinality);
+            int nextSetBit = -1;
+            for (int i=0; i < cardinality; i++) {
+                nextSetBit = bitSet.nextSetBit(nextSetBit + 1);
+                assertEquals(array[i], nextSetBit);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Random/RandomStreamTest.java	Thu May 02 09:18:56 2013 -0700
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ * 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.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Random;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * @test
+ * @run testng RandomStreamTest
+ * @summary test stream methods on Random
+ * @author Brian Goetz
+ */
+public class RandomStreamTest {
+
+    private static final int SIZE = 1000;
+
+    @DataProvider(name = "suppliers")
+    public Object[][] randomSuppliers() {
+        return new Object[][] {
+            {new Random(), SIZE},
+            {new SecureRandom(), SIZE}
+        };
+    }
+
+    @Test(dataProvider = "suppliers")
+    public void testRandomIntStream(final Random random, final int count) {
+        final List<Integer> destination = new ArrayList<>(count);
+        random.ints().limit(count).forEach(destination::add);
+        assertEquals(destination.size(), count);
+    }
+
+    @Test(dataProvider = "suppliers")
+    public void testRandomLongStream(final Random random, final int count) {
+        final List<Long> destination = new ArrayList<>(count);
+        random.longs().limit(count).forEach(destination::add);
+        assertEquals(destination.size(), count);
+    }
+
+    @Test(dataProvider = "suppliers")
+    public void testRandomDoubleStream(final Random random, final int count) {
+        final List<Double> destination = new ArrayList<>(count);
+        random.doubles().limit(count).forEach(destination::add);
+        random.doubles().limit(count).forEach(d -> assertTrue(d >= 0.0 && d < 1.0));
+        assertEquals(destination.size(), count);
+    }
+
+    @Test(dataProvider = "suppliers")
+    public void testRandomGaussianStream(final Random random, final int count) {
+        final List<Double> destination = new ArrayList<>(count);
+        random.gaussians().limit(count).forEach(destination::add);
+        assertEquals(destination.size(), count);
+    }
+
+    @Test
+    public void testIntStream() {
+        final long seed = System.currentTimeMillis();
+        final Random r1 = new Random(seed);
+        final int[] a = new int[SIZE];
+        for (int i=0; i < SIZE; i++) {
+            a[i] = r1.nextInt();
+        }
+
+        final Random r2 = new Random(seed); // same seed
+        final int[] b = r2.ints().limit(SIZE).toArray();
+        assertEquals(a, b);
+    }
+
+    @Test
+    public void testLongStream() {
+        final long seed = System.currentTimeMillis();
+        final Random r1 = new Random(seed);
+        final long[] a = new long[SIZE];
+        for (int i=0; i < SIZE; i++) {
+            a[i] = r1.nextLong();
+        }
+
+        final Random r2 = new Random(seed); // same seed
+        final long[] b = r2.longs().limit(SIZE).toArray();
+        assertEquals(a, b);
+    }
+
+    @Test
+    public void testDoubleStream() {
+        final long seed = System.currentTimeMillis();
+        final Random r1 = new Random(seed);
+        final double[] a = new double[SIZE];
+        for (int i=0; i < SIZE; i++) {
+            a[i] = r1.nextDouble();
+        }
+
+        final Random r2 = new Random(seed); // same seed
+        final double[] b = r2.doubles().limit(SIZE).toArray();
+        assertEquals(a, b);
+    }
+
+    @Test
+    public void testGaussianStream() {
+        final long seed = System.currentTimeMillis();
+        final Random r1 = new Random(seed);
+        final double[] a = new double[SIZE];
+        for (int i=0; i < SIZE; i++) {
+            a[i] = r1.nextGaussian();
+        }
+
+        final Random r2 = new Random(seed); // same seed
+        final double[] b = r2.gaussians().limit(SIZE).toArray();
+        assertEquals(a, b);
+    }
+
+    @Test
+    public void testThreadLocalIntStream() throws InterruptedException {
+        final ExecutorService e = Executors.newFixedThreadPool(10);
+        final ThreadLocalRandom tlr = ThreadLocalRandom.current();
+
+        final class RandomTask implements Runnable {
+            int[] randoms;
+
+            @Override
+            public void run() {
+                randoms = tlr.ints().limit(SIZE).toArray();
+            }
+        }
+        final RandomTask[] tasks = new RandomTask[10];
+        for (int i=0; i < tasks.length; i++) {
+            tasks[i] = new RandomTask();
+        }
+        for (int i=0; i < tasks.length; i++) {
+            e.submit(tasks[i]);
+        }
+        e.shutdown();
+        e.awaitTermination(3, TimeUnit.SECONDS);
+        for (int i=1; i < tasks.length; i++) {
+            assertFalse(Arrays.equals(tasks[0].randoms, tasks[i].randoms));
+        }
+    }
+
+    @Test
+    public void testThreadLocalLongStream() throws InterruptedException {
+        final ExecutorService e = Executors.newFixedThreadPool(10);
+        final ThreadLocalRandom tlr = ThreadLocalRandom.current();
+
+        final class RandomTask implements Runnable {
+            long[] randoms;
+
+            @Override
+            public void run() {
+                randoms = tlr.longs().limit(SIZE).toArray();
+            }
+        }
+        final RandomTask[] tasks = new RandomTask[10];
+        for (int i=0; i < tasks.length; i++) {
+            tasks[i] = new RandomTask();
+        }
+        for (int i=0; i < tasks.length; i++) {
+            e.submit(tasks[i]);
+        }
+        e.shutdown();
+        e.awaitTermination(3, TimeUnit.SECONDS);
+        for (int i=1; i < tasks.length; i++) {
+            assertFalse(Arrays.equals(tasks[0].randoms, tasks[i].randoms));
+        }
+    }
+
+    @Test
+    public void testThreadLocalDoubleStream() throws InterruptedException {
+        final ExecutorService e = Executors.newFixedThreadPool(10);
+        final ThreadLocalRandom tlr = ThreadLocalRandom.current();
+
+        final class RandomTask implements Runnable {
+            double[] randoms;
+
+            @Override
+            public void run() {
+                randoms = tlr.doubles().limit(SIZE).toArray();
+            }
+        }
+        final RandomTask[] tasks = new RandomTask[10];
+        for (int i=0; i < tasks.length; i++) {
+            tasks[i] = new RandomTask();
+        }
+        for (int i=0; i < tasks.length; i++) {
+            e.submit(tasks[i]);
+        }
+        e.shutdown();
+        e.awaitTermination(3, TimeUnit.SECONDS);
+        for (int i=1; i < tasks.length; i++) {
+            assertFalse(Arrays.equals(tasks[0].randoms, tasks[i].randoms));
+        }
+    }
+
+    @Test
+    public void testThreadLocalGaussianStream() throws InterruptedException {
+        final ExecutorService e = Executors.newFixedThreadPool(10);
+        final ThreadLocalRandom tlr = ThreadLocalRandom.current();
+
+        final class RandomTask implements Runnable {
+            double[] randoms;
+
+            @Override
+            public void run() {
+                randoms = tlr.gaussians().limit(SIZE).toArray();
+            }
+        }
+        final RandomTask[] tasks = new RandomTask[10];
+        for (int i=0; i < tasks.length; i++) {
+            tasks[i] = new RandomTask();
+        }
+        for (int i=0; i < tasks.length; i++) {
+            e.submit(tasks[i]);
+        }
+        e.shutdown();
+        e.awaitTermination(3, TimeUnit.SECONDS);
+        for (int i=1; i < tasks.length; i++) {
+            assertFalse(Arrays.equals(tasks[0].randoms, tasks[i].randoms));
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/zip/ZipFile/StreamZipEntriesTest.java	Thu May 02 09:18:56 2013 -0700
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ * 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.
+ */
+
+/**
+ * @test
+ * @run testng StreamZipEntriesTest
+ * @summary Make sure we can stream entries of a zip file.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.Object;
+import java.lang.System;
+import java.util.jar.JarFile;
+import java.util.jar.JarEntry;
+import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+public class StreamZipEntriesTest {
+
+    @Test
+    public void testStreamZip() throws IOException {
+        try (ZipFile zf = new ZipFile(new File(System.getProperty("test.src", "."), "input.zip"))) {
+            zf.stream().forEach(e -> assertTrue(e instanceof ZipEntry));
+            zf.stream().forEach(e -> assertEquals(e.toString(), "ReadZip.java"));
+
+            Object elements[] = zf.stream().toArray();
+            assertEquals(1, elements.length);
+            assertEquals(elements[0].toString(), "ReadZip.java");
+        }
+    }
+
+    @Test
+    public void testStreamJar() throws IOException {
+        try (JarFile jf = new JarFile(new File(System.getProperty("test.src", "."), "input.jar"))) {
+            jf.stream().forEach(e -> assertTrue(e instanceof JarEntry));
+
+            Object elements[] = jf.stream().toArray();
+            assertEquals(3, elements.length);
+            assertEquals(elements[0].toString(), "META-INF/");
+            assertEquals(elements[1].toString(), "META-INF/MANIFEST.MF");
+            assertEquals(elements[2].toString(), "ReleaseInflater.java");
+        }
+    }
+
+    @Test
+    public void testClosedZipFile() throws IOException {
+        ZipFile zf = new ZipFile(new File(System.getProperty("test.src", "."), "input.zip"));
+        zf.close();
+        try {
+            Stream s = zf.stream();
+            fail("Should have thrown IllegalStateException");
+        } catch (IllegalStateException e) {
+            // expected;
+        }
+    }
+
+    @Test
+    public void testClosedJarFile() throws IOException {
+        JarFile jf = new JarFile(new File(System.getProperty("test.src", "."), "input.jar"));
+        jf.close();
+        try {
+            Stream s = jf.stream();
+            fail("Should have thrown IllegalStateException");
+        } catch (IllegalStateException e) {
+            // expected;
+        }
+    }
+}