changeset 7603:88776f695148

More spec; remove Arrays.indices()
author briangoetz
date Mon, 11 Mar 2013 22:20:29 -0400
parents 61c8d9f1c754
children 40c0a71455d7 62464bba05b2 34e0d5420ce9
files src/share/classes/java/util/Arrays.java src/share/classes/java/util/stream/Collector.java src/share/classes/java/util/stream/Collectors.java src/share/classes/java/util/stream/Stream.java
diffstat 4 files changed, 62 insertions(+), 74 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/util/Arrays.java	Mon Mar 11 11:59:33 2013 -0400
+++ b/src/share/classes/java/util/Arrays.java	Mon Mar 11 22:20:29 2013 -0400
@@ -4444,42 +4444,6 @@
         }
     }
 
-    public static<T> IntStream indices(T[] array) {
-        return Streams.intRange(0, array.length);
-    }
-
-    public static IntStream indices(int[] array) {
-        return Streams.intRange(0, array.length);
-    }
-
-    public static IntStream indices(long[] array) {
-        return Streams.intRange(0, array.length);
-    }
-
-    public static IntStream indices(double[] array) {
-        return Streams.intRange(0, array.length);
-    }
-
-    public static IntStream indices(short[] array) {
-        return Streams.intRange(0, array.length);
-    }
-
-    public static IntStream indices(byte[] array) {
-        return Streams.intRange(0, array.length);
-    }
-
-    public static IntStream indices(boolean[] array) {
-        return Streams.intRange(0, array.length);
-    }
-
-    public static IntStream indices(float[] array) {
-        return Streams.intRange(0, array.length);
-    }
-
-    public static IntStream indices(char[] array) {
-        return Streams.intRange(0, array.length);
-    }
-
     /**
      * Produce a {@link Spliterator} for the contents of the specified array.
      *
--- a/src/share/classes/java/util/stream/Collector.java	Mon Mar 11 11:59:33 2013 -0400
+++ b/src/share/classes/java/util/stream/Collector.java	Mon Mar 11 22:20:29 2013 -0400
@@ -32,36 +32,63 @@
 import java.util.function.Supplier;
 
 /**
- * A (possibly parallel) reduction operation that folds input elements into a mutable result container.  Examples
+ * A reduction operation that folds input elements into a mutable result container.  Examples
  * of such operations include accumulating input elements into a {@code Collection}; concatenating strings into
  * a {@code StringBuilder}; computing summary information about elements such as sum, min, max, or average;
- * computing "pivot table" summaries such as "maximum valued transaction by seller", etc.
+ * computing "pivot table" summaries such as "maximum valued transaction by seller", etc.  Reduction operations
+ * can be performed either sequentially or in parallel.
  *
  * A {@code Collector} is a triple of functions that describe: how to create a new result container, how to
  * incorporate a new data element into a result container, and how to combine two result containers into one.
  * The last function -- combining two containers into one -- is used during parallel operations, where we
  * collect subsets of the input in parallel, and the merge the subresults into a combined result.
  *
- * @apiNote
- * An operation that can be easily modeled by {@code Collector} is accumulating elements into a {@code TreeSet}.
- * In this case, the @{code resultSupplier()} function is {@code new Treeset<T>()}, the {@code accumulator}
- * function is {@code (set, element) -> set.add(element)}, and the combiner function is
+ * <p>An example of an operation that can be easily modeled by {@code Collector} is accumulating elements into a
+ * {@code TreeSet}. In this case, the @{code resultSupplier()} function is {@code new Treeset<T>()}, the
+ * {@code accumulator} function is {@code (set, element) -> set.add(element)}, and the combiner function is
  * {@code (left, right) -> { left.addAll(right); return left; }}.  (This behavior is implemented by the method
  * {@code Collectors.toCollection(TreeSet::new)}).
  *
+ * @@@ Document behavior of collect()
+ * -- will only pass to combiner / accumulator what was returned from supplier
+ * -- will not modify container after combining
+ * -- for non-concurrent collectors, result containers are kept isolated
+ * @@@ Document concurrent behavior and interaction with ordering
+ *
  * @see Stream#collect(Collector)
  * @see Stream#collectUnordered(Collector)
  * @see Collectors
  *
  * @param <T> The type of input element to the collect operation
- * @param <R> THe result type of the collect operation
+ * @param <R> The result type of the collect operation
  * @since 1.8
  */
 public interface Collector<T, R> {
+    /**
+     * A function that creates and return a new, empty result container.
+     * @return A function which, when invoked, returns a new, empty result container on each invocation
+     */
     Supplier<R> resultSupplier();
+
+    /**
+     * A function that accepts a result container and a value and incorporates the value into the container.
+     * @return A function which, when invoked with a result container and a value, modifies the state of the reslut
+     * container to reflect incorporation of the new value
+     */
     BiConsumer<R, T> accumulator();
+
+    /**
+     * A function that accepts two result containers and combines their states.  It may return a new container,
+     * or may merge the state of one container into the other, and return the modified container.
+     * @return
+     */
     BinaryOperator<R> combiner();
 
+    /**
+     * Indicates that this collector is <em>concurrent</em>, meaning that the result container can support the
+     * accumulator function being called concurrently with the same result container from multiple threads.
+     * @return True if this collector supports concurrent accumulation
+     */
     default boolean isConcurrent() { return false; }
 
     interface OfInt<R> extends Collector<Integer, R> {
--- a/src/share/classes/java/util/stream/Collectors.java	Mon Mar 11 11:59:33 2013 -0400
+++ b/src/share/classes/java/util/stream/Collectors.java	Mon Mar 11 22:20:29 2013 -0400
@@ -69,6 +69,14 @@
 
     private Collectors() {}
 
+    /**
+     * A merge function, suitable for use in {@link Map#merge(Object, Object, BiFunction)} or
+     * {@link #toMap(Function, Supplier, BinaryOperator)}, which always throws {@code IllegalStateException}.
+     * This can be used to enforce the assumption that the elements being collected are distinct.
+     *
+     * @param <T> The type of input arguments to the merge function
+     * @return A merge function which always throw {@code IllegalStateException}
+     */
     public static<T> BinaryOperator<T> throwingMergeFunction() {
         return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };
     }
@@ -86,10 +94,6 @@
             this.isConcurrent = isConcurrent;
         }
 
-        AbstractCollectorImpl(Supplier<R> resultSupplier, BinaryOperator<R> combiner) {
-            this(resultSupplier, combiner, false);
-        }
-
         public Supplier<R> resultSupplier() {
             return resultSupplier;
         }
@@ -114,10 +118,6 @@
             this.accumulator = accumulator;
         }
 
-        CollectorImpl(Supplier<R> resultSupplier, BiConsumer<R, T> accumulator, BinaryOperator<R> combiner) {
-            this(resultSupplier, accumulator, combiner, false);
-        }
-
         @Override
         public BiConsumer<R, T> accumulator() {
             return accumulator;
@@ -135,10 +135,6 @@
             this.intAccumulator = accumulator;
         }
 
-        IntCollectorImpl(Supplier<R> resultSupplier, ObjIntConsumer<R> accumulator, BinaryOperator<R> combiner) {
-            this(resultSupplier, accumulator, combiner, false);
-        }
-
         @Override
         public ObjIntConsumer<R> intAccumulator() {
             return intAccumulator;
@@ -156,10 +152,6 @@
             this.doubleAccumulator = accumulator;
         }
 
-        DoubleCollectorImpl(Supplier<R> resultSupplier, ObjDoubleConsumer<R> accumulator, BinaryOperator<R> combiner) {
-            this(resultSupplier, accumulator, combiner, false);
-        }
-
         @Override
         public ObjDoubleConsumer<R> doubleAccumulator() {
             return doubleAccumulator;
@@ -177,10 +169,6 @@
             this.longAccumulator = accumulator;
         }
 
-        LongCollectorImpl(Supplier<R> resultSupplier, ObjLongConsumer<R> accumulator, BinaryOperator<R> combiner) {
-            this(resultSupplier, accumulator, combiner, false);
-        }
-
         @Override
         public ObjLongConsumer<R> longAccumulator() {
             return longAccumulator;
@@ -191,7 +179,7 @@
     static<T, R> Collector<T, R> leftCombining(Supplier<R> supplier,
                                                BiConsumer<R, T> accumulator,
                                                BiConsumer<R, R> merger) {
-        return new CollectorImpl<>(supplier, accumulator, (r1, r2) -> { merger.accept(r1, r2); return r1; });
+        return new CollectorImpl<>(supplier, accumulator, (r1, r2) -> { merger.accept(r1, r2); return r1; }, false);
     }
 
     /**
@@ -338,7 +326,7 @@
                     downstreamAccumulator.accept(m.computeIfAbsent(key, k -> downstreamSupplier.get()), t);
                 };
                 return new CollectorImpl<>((Supplier<Map<K, D>>) mapFactory, accumulator,
-                                           Collectors.<K, D, Map<K, D>>leftMapMerger(downstreamCombiner));
+                                           Collectors.<K, D, Map<K, D>>leftMapMerger(downstreamCombiner), false);
             }
 
             @Override
@@ -354,7 +342,7 @@
                               mapper.apply(value), reducer);
                 };
                 return new CollectorImpl<>((Supplier<Map<K, U>>) mapFactory, accumulator,
-                                           Collectors.<K, U, Map<K, U>>leftMapMerger(reducer));
+                                           Collectors.<K, U, Map<K, U>>leftMapMerger(reducer), false);
             }
 
             @Override
@@ -444,7 +432,7 @@
                 };
                 return new CollectorImpl<>(() -> new Partition<>(downstream.resultSupplier().get(),
                                                                  downstream.resultSupplier().get()),
-                                           accumulator, leftPartitionMerger(downstream.combiner()));
+                                           accumulator, leftPartitionMerger(downstream.combiner()), false);
             }
 
             @Override
@@ -464,7 +452,7 @@
                         asPartition.forFalse = reducer.apply(asPartition.forFalse, mapper.apply(t));
                 };
                 return new CollectorImpl<>(() -> new Partition<>(identity, identity),
-                                           accumulator, leftPartitionMerger(reducer));
+                                           accumulator, leftPartitionMerger(reducer), false);
             }
 
             @Override
@@ -591,7 +579,7 @@
                                                                     Supplier<M> mapSupplier,
                                                                     BinaryOperator<U> mergeFunction) {
         BiConsumer<M, T> accumulator = (map, value) -> map.merge(value, mapper.apply(value), mergeFunction);
-        return new CollectorImpl<>(mapSupplier, accumulator, leftMapMerger(mergeFunction));
+        return new CollectorImpl<>(mapSupplier, accumulator, leftMapMerger(mergeFunction), false);
     }
 
     /**
@@ -833,7 +821,7 @@
      */
     public static Collector.OfLong<LongStatistics> toLongStatistics() {
         return new LongCollectorImpl<>(LongStatistics::new, LongStatistics::accept,
-                                      (l, r) -> { l.combine(r); return l; });
+                                      (l, r) -> { l.combine(r); return l; }, false);
     }
 
     /**
@@ -842,7 +830,7 @@
      */
     public static Collector.OfDouble<DoubleStatistics> toDoubleStatistics() {
         return new DoubleCollectorImpl<>(DoubleStatistics::new, DoubleStatistics::accept,
-                                         (l, r) -> { l.combine(r); return l; });
+                                         (l, r) -> { l.combine(r); return l; }, false);
     }
 
     /**
--- a/src/share/classes/java/util/stream/Stream.java	Mon Mar 11 11:59:33 2013 -0400
+++ b/src/share/classes/java/util/stream/Stream.java	Mon Mar 11 22:20:29 2013 -0400
@@ -347,6 +347,10 @@
      *     Integer sum = integers.reduce(0, Integer::sum);
      * </pre>
      *
+     * <p>While this may seem a more roundabout way to perform an aggregation compared to simply mutating a running
+     * total in a loop, reduction operations parallelize more gracefully, without needing additional synchronization
+     * and with greatly reduced risk of data races.
+     *
      * @param identity The identity value for the reducing function
      * @param reducer An associative function for combining two elements
      * @return The result of the reduction
@@ -424,7 +428,7 @@
     /**
      * Perform a <em>mutable reduction</em> operation on the elements of this stream.  A mutable reduction is one
      * in which the reduced value is a mutable value holder, such as an {@code ArrayList}, and elements are incorporated
-     * by updating the state of the result, rather than by replacing the result.  This is equivalent to:
+     * by updating the state of the result, rather than by replacing the result.  This produces a result equivalent to:
      * <pre>
      *     R result = resultFactory.get();
      *     for (T element : this stream)
@@ -432,7 +436,8 @@
      *     return result;
      * </pre>
      *
-     * but is not constrained to execute sequentially.
+     * Like {@link #reduce(Object, BinaryOperator)}, {@code collect} operations can be parallelized without
+     * requiring additional sychronization.
      *
      * @apiNote There are many existing classes in the JDK whose signatures are a good match for use as
      * arguments to {@code collect()}.  For example, the following will accumulate strings into an ArrayList:
@@ -460,9 +465,13 @@
 
     /** Perform a <em>mutable reduction</em> operation on the elements of this stream using a {@code Collector}
      * object to describe the reduction.  A {@code Collector} encapsulates the functions used as arguments
-     * to {@link #collect(Supplier, BiConsumer, BiConsumer)}, allowing for reuse of collection strategies
+     * to {@link #collect(Supplier, BiConsumer, BiConsumer)}, allowing for reuse of collection strategies,
      * and composition of collect operations such as multiple-level group-by.
      *
+     * @@@ always non-concurrent / isolated
+     * @@@ interplay between resultSupplier, accumulator, combiner
+     * @@@ container not modified after combination
+     *
      * @apiNote
      * The following will accumulate strings into an ArrayList:
      * <pre>