changeset 7827:c537f01643b4

More package-doc and stream specification
author briangoetz
date Fri, 05 Apr 2013 14:44:30 -0400
parents cb2c69a86f0f
children 360ebf593040
files src/share/classes/java/nio/file/Files.java src/share/classes/java/util/stream/BaseStream.java src/share/classes/java/util/stream/Stream.java src/share/classes/java/util/stream/package-info.java
diffstat 4 files changed, 120 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/nio/file/Files.java	Fri Apr 05 10:50:31 2013 -0700
+++ b/src/share/classes/java/nio/file/Files.java	Fri Apr 05 14:44:30 2013 -0400
@@ -3220,7 +3220,7 @@
             }
         };
 
-        return new DelegatingCloseableStream(ds,
+        return new DelegatingCloseableStream<>(ds,
             Streams.stream(Spliterators.spliteratorUnknownSize(it,
                                                                Spliterator.DISTINCT)));
     }
@@ -3312,7 +3312,7 @@
         }
 
         FileTreeIterator itor = FileTreeIterator.iterator(start, maxDepth, options);
-        return new DelegatingCloseableStream(itor,
+        return new DelegatingCloseableStream<>(itor,
             Streams.stream(Spliterators.spliteratorUnknownSize(itor, Spliterator.DISTINCT))
                    .map(entry -> entry.getPath()));
     }
@@ -3409,7 +3409,7 @@
             throw new IllegalArgumentException("'maxDepth' is negative");
         }
         FileTreeIterator itor = FileTreeIterator.iterator(start, maxDepth, options);
-        return new DelegatingCloseableStream(itor,
+        return new DelegatingCloseableStream<>(itor,
             Streams.stream(Spliterators.spliteratorUnknownSize(itor, Spliterator.DISTINCT))
                    .filter(entry -> matcher.test(entry.getPath(), entry.getFileAttributes()))
                    .map(entry -> entry.getPath()));
@@ -3462,6 +3462,6 @@
         throws IOException
     {
         BufferedReader br = Files.newBufferedReader(path, cs);
-        return new DelegatingCloseableStream(br, br.lines());
+        return new DelegatingCloseableStream<>(br, br.lines());
     }
 }
--- a/src/share/classes/java/util/stream/BaseStream.java	Fri Apr 05 10:50:31 2013 -0700
+++ b/src/share/classes/java/util/stream/BaseStream.java	Fri Apr 05 14:44:30 2013 -0400
@@ -30,8 +30,8 @@
 /**
  * Base interface for stream types such as {@link Stream}, {@link IntStream},
  * etc.  Contains methods common to all stream types.  Many of these methods
- * are implemented by {@link AbstractPipeline}, even though {@code AbstractPipeline}
- * does not directly implement {@code BaseStream}.
+ * are implemented by {@link AbstractPipeline}, even though
+ * {@code AbstractPipeline} does not directly implement {@code BaseStream}.
  *
  * @param <T> Type of stream elements.
  * @param <S> Type of stream implementing {@code BaseStream}.
@@ -41,7 +41,8 @@
     /**
      * Returns an iterator for the elements of this stream.
      *
-     * <p>This is a <a href="package-summary.html#StreamOps">terminal operation</a>.
+     * <p>This is a <a href="package-summary.html#StreamOps">terminal
+     * operation</a>.
      *
      * @return the element iterator for this stream
      */
@@ -50,48 +51,56 @@
     /**
      * Returns a spliterator for the elements of this stream.
      *
-     * <p>This is a <a href="package-summary.html#StreamOps">terminal operation</a>.
+     * <p>This is a <a href="package-summary.html#StreamOps">terminal
+     * operation</a>.
      *
      * @return the element spliterator for this stream
      */
     Spliterator<T> spliterator();
 
     /**
-     * Returns whether this stream, when executed, would execute in parallel (assuming
-     * no further modification of the stream, such as appending further intermediate
-     * operations or changing its parallelism).  Calling this method after invoking
-     * an intermediate or terminal stream operation method may yield unpredictable results.
+     * Returns whether this stream, when executed, would execute in parallel
+     * (assuming no further modification of the stream, such as appending
+     * further intermediate operations or changing its parallelism).  Calling
+     * this method after invoking an intermediate or terminal stream operation
+     * method may yield unpredictable results.
      *
-     * @return whether this stream would execute in parallel if executed without further
-     * modification
+     * @return whether this stream would execute in parallel if executed without
+     * further modification
      */
     boolean isParallel();
 
     /**
-     * Produces an equivalent stream that is sequential.
-     * If this stream is already sequential, may return itself.
+     * Produces an equivalent stream that is sequential.  May return
+     * itself, either because the stream was already sequential, or because
+     * the underlying stream state was modified to be sequential.
      *
-     * <p>This is an <a href="package-summary.html#StreamOps">intermediate operation</a>.
+     * <p>This is an <a href="package-summary.html#StreamOps">intermediate
+     * operation</a>.
      *
      * @return a sequential stream
      */
     S sequential();
 
     /**
-     * Produces an equivalent stream that is parallel.
-     * If this stream is already parallel, may return itself.
+     * Produces an equivalent stream that is parallel.  May return
+     * itself, either because the stream was already parallel, or because
+     * the underlying stream state was modified to be parallel.
      *
-     * <p>This is an <a href="package-summary.html#StreamOps">intermediate operation</a>.
+     * <p>This is an <a href="package-summary.html#StreamOps">intermediate
+     * operation</a>.
      *
      * @return a parallel stream
      */
     S parallel();
 
     /**
-     * Produces an equivalent stream that is unordered. If this stream is
-     * already unordered, may return itself.
+     * Produces an equivalent stream that is
+     * <a href="package-summary.html#Ordering">unordered</a>.  May return
+     * itself if the stream was already unordered.
      *
-     * <p>This is an <a href="package-summary.html#StreamOps">intermediate operation</a>.
+     * <p>This is an <a href="package-summary.html#StreamOps">intermediate
+     * operation</a>.
      * @return an unordered stream
      */
     S unordered();
--- a/src/share/classes/java/util/stream/Stream.java	Fri Apr 05 10:50:31 2013 -0700
+++ b/src/share/classes/java/util/stream/Stream.java	Fri Apr 05 14:44:30 2013 -0400
@@ -62,7 +62,18 @@
  * stream operations preserve the <a href="package-summary.html#Ordering">
  * encounter order</a> of their source, and terminal operations
  * respect the encounter order of their source, if the source
- * has an encounter order.
+ * has an encounter order.  Provided that and parameters to stream operations
+ * satisfy the <a href="package-summary.html#NonInterference">non-interference
+ * requirements</a>, and excepting differences arising from the absence of
+ * a defined encounter order, the result of a stream pipeline should be the
+ * stable across multiple executions of the same operations on the same source.
+ * However, the timing and thread in which side-effects occur (for those
+ * operations which are allowed to produce side-effects, such as
+ * {@link #forEach(Consumer)}), are explicitly nondeterministic for parallel
+ * execution of stream pipelines.
+ *
+ * <p>Unless otherwise noted, passing a {@code null} argument to any stream
+ * method may result in a {@link NullPointerException}.
  *
  * @apiNote
  * Streams are not data structures; they do not manage the storage for their
@@ -70,9 +81,6 @@
  * you can use the {@link #iterator()} or {@link #spliterator()} operations to
  * perform a controlled traversal.
  *
- * <p>Unless otherwise noted, passing a {@code null} argument to any stream
- * method may result in a {@link NullPointerException}.
- *
  * @param <T> Type of elements.
  * @since 1.8
  * @see <a href="package-summary.html">java.util.stream</a>
@@ -759,6 +767,7 @@
      *
      * @return An {@code Optional} describing the first element of this stream,
      * or an empty {@code Optional} if the stream is empty
+     * @throws NullPointerException if the element selected is null
      */
     Optional<T> findFirst();
 
@@ -777,6 +786,7 @@
      *
      * @return An {@code Optional} describing some element of this stream, or an
      * empty {@code Optional} if the stream is empty
+     * @throws NullPointerException if the element selected is null
      * @see #findFirst()
      */
     Optional<T> findAny();
--- a/src/share/classes/java/util/stream/package-info.java	Fri Apr 05 10:50:31 2013 -0700
+++ b/src/share/classes/java/util/stream/package-info.java	Fri Apr 05 14:44:30 2013 -0400
@@ -163,26 +163,42 @@
  *
  * <h3><a name="Ordering">Ordering</a></h3>
  *
- * <p>Streams may or may not have an <em>encounter order</em>.  Whether or not there is an
- * encounter order depends on the source, the intermediate operations, and the terminal
- * operation.  Certain stream sources (such as {@code List} or arrays) are intrinsically ordered,
- * whereas others (such as {@code HashSet}) are not.  Some intermediate operations may impose
- * an encounter order on an otherwise unordered stream, such as
- * {@link java.util.stream.Stream#sorted()}.  Some intermediate operations may remove the
- * constraint of ordering, rendering unordered a previously ordered stream.  Finally, some
- * terminal operations may ignore encounter order, such as {@link java.util.stream.Stream#forEach},
- * and others may have optimized implementations for the case where there is no defined
- * encounter order.
+ * <p>Streams may or may not have an <em>encounter order</em>.  Whether or not
+ * there is an encounter order depends on the source, the intermediate
+ * operations, and the terminal operation.  Certain stream sources (such as
+ * {@code List} or arrays) are intrinsically ordered, whereas others (such as
+ * {@code HashSet}) are not.  Some intermediate operations may impose an
+ * encounter order on an otherwise unordered stream, such as
+ * {@link java.util.stream.Stream#sorted()}, and others may render an ordered
+ * stream unordered (such as {@link {@link java.util.stream.Stream#unordered()}}).
+ * Some terminal operations may ignore encounter order, such as
+ * {@link java.util.stream.Stream#forEach}.
  *
- * <p>If a Stream is ordered, most operations are constrained to operate on the elements in their
- * encounter order; if the source of a stream is a {@code List} containing {@code [1, 2, 3]},
- * then the result of executing {@code map(x -> x*2)} must be {@code [2, 4, 6]}.  However, if
- * the source has no defined encounter order, than any permutation of the values {@code [2, 4, 6]}
- * would be a valid result. Many operations can still be efficiently parallelized even under
- * ordering constraints, but some (such as duplicate removal) may be more efficient without
+ * <p>If a Stream is ordered, most operations are constrained to operate on the
+ * elements in their encounter order; if the source of a stream is a {@code List}
+ * containing {@code [1, 2, 3]}, then the result of executing {@code map(x -> x*2)}
+ * must be {@code [2, 4, 6]}.  However, if the source has no defined encounter
+ * order, than any permutation of the values {@code [2, 4, 6]} would be a valid
+ * result. Many operations can still be efficiently parallelized even under
  * ordering constraints.
  *
- * TODO Interaction between ordering and concurrency
+ * <p>For sequential streams, ordering is only relevant to the determinism
+ * of operations performed repeatedly on the same source.  (An {@code ArrayList}
+ * is constrained to iterate elements in order; a {@code HashSet} is not, and
+ * repeated iteration might produce a different order.)
+ *
+ * <p>For parallel streams, relaxing the ordering constraint can enable
+ * optimized implementation for some operations.  For example, duplicate
+ * filtration on an ordered stream must completely process the first partition
+ * before it can return any elements from a subsequent partition, even if those
+ * elements are available earlier.  On the other hand, without the constraint of
+ * ordering, duplicate filtration can be done more efficiently by using
+ * a shared {@code ConcurrentHashSet}.  There will be cases where the stream
+ * is structurally ordered (the source is ordered and the intermediate
+ * operations are order-preserving), but the user does not particularly care
+ * about the encounter order.  In some cases, explicitly de-ordering the stream
+ * with the {@link java.util.stream.Stream#unordered()} method may result in
+ * improved parallel performance for some stateful or terminal operations.
  *
  * <h2><a name="Non-Interference">Non-interference</h2>
  *
@@ -409,6 +425,43 @@
  *                                          .collect(Collectors.toList());
  * </pre>
  *
+ * <h3><a name="ConcurrentReduction">Reduction, concurrency, and ordering</a></h3>
+ *
+ * With some complex reduction operations, such as those that produce a
+ * {@code Map}, such as:
+ * <pre>
+ *     Map<Buyer, List<Transaction>> salesByBuyer
+ *         = txns.stream().collect(groupingBy(Transaction::getBuyer));
+ * </pre>
+ *
+ * it may actually be counterproductive to perform this reduction in parallel,
+ * because the merging step (merging one {@code Map} into another by key)
+ * can be expensive for some {@code Map} implementations.
+ *
+ * <p>If you are willing to relax the constraint or ordering, there is another
+ * possibility -- perform a <em>concurrent</em> reduction.  This can be done if
+ * the result container can safely be updated concurrently, such as one that
+ * collects to a {@code ConcurrentHashMap}.  If it is important that the
+ * elements for a given key appear in the order they appear in the source, then
+ * we are constrained to implement either a sequential reduction or a
+ * merge-based parallel reduction.  But, if this ordering constraint is
+ * relaxed, we can also choose to have a shared result container, and let many
+ * threads update the result container at once, obviating the need for the
+ * expensive merging step.  The
+ * {@link java.util.stream.Stream#collect(Supplier, BiConsumer, BiConsumer)}
+ * implementation will choose a concurrent collection if the stream is
+ * parallel, the {@link java.util.stream.Collector} has the
+ * {@link java.util.stream.Collector.Characteristics.CONCURRENT} characteristic,
+ * and either the stream is unordered or the collector has the
+ * {@link java.util.stream.Collector.Characteristics.UNORDERED} characteristic,
+ * as in:
+ * <pre>
+ *     Map<Buyer, List<Transaction>> salesByBuyer
+ *         = txns.stream()
+ *               .unordered()
+ *               .collect(groupingByConcurrent(Transaction::getBuyer));
+ * </pre>
+ *
  * <a name="Associativity"><h2>Associativity</h2></a>
  *
  * An operator or function {@code op} is <em>associative</em> if the following holds:
@@ -423,8 +476,8 @@
  * the results.
  * TODO what does associative mean for mutative combining functions?
  *
+ * <h2><a name="StreamSources">Stream sources</a></h2>
  * TODO where does this section go?
- * <h2><a name="StreamSources">Stream sources</a></h2>
  *
  * XXX - change to section to stream construction gradually introducing more
  *       complex ways to construct
@@ -486,3 +539,6 @@
  */
 
 package java.util.stream;
+
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
\ No newline at end of file