--- old/src/share/classes/java/util/stream/Collectors.java 2013-07-05 16:27:34.174022748 -0700 +++ new/src/share/classes/java/util/stream/Collectors.java 2013-07-05 16:27:30.506022813 -0700 @@ -27,6 +27,7 @@ import java.util.AbstractMap; import java.util.AbstractSet; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -39,14 +40,16 @@ import java.util.List; import java.util.LongSummaryStatistics; import java.util.Map; -import java.util.NoSuchElementException; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.StringJoiner; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.BinaryOperator; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; @@ -64,20 +67,21 @@ * mutable reduction tasks: * *
{@code - * // Accumulate elements into a List - * List* @@ -103,15 +107,19 @@ */ public final class Collectors { - private static final Setlist = people.collect(Collectors.toList()); + * // Accumulate names into a List + * List list = people.stream().map(Person::getName).collect(Collectors.toList()); * - * // Accumulate elements into a TreeSet - * List list = people.collect(Collectors.toCollection(TreeSet::new)); + * // Accumulate names into a TreeSet + * Set list = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new)); * * // Convert elements to strings and concatenate them, separated by commas - * String joined = stream.map(Object::toString) - * .collect(Collectors.toStringJoiner(", ")) - * .toString(); + * String joined = things.stream() + * .map(Object::toString) + * .collect(Collectors.joining(", ")); * * // Find highest-paid employee * Employee highestPaid = employees.stream() - * .collect(Collectors.maxBy(Comparator.comparing(Employee::getSalary))); + * .collect(Collectors.maxBy(Comparator.comparing(Employee::getSalary))) + * .get(); * * // Group employees by department * Map > byDept @@ -85,7 +89,7 @@ * .collect(Collectors.groupingBy(Employee::getDepartment)); * * // Find highest-paid employee by department - * Map highestPaidByDept + * Map > highestPaidByDept * = employees.stream() * .collect(Collectors.groupingBy(Employee::getDepartment, * Collectors.maxBy(Comparator.comparing(Employee::getSalary)))); @@ -93,7 +97,7 @@ * // Partition students into passing and failing * Map > passingFailing = * students.stream() - * .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD); + * .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD)); * * }
{@code @@ -364,23 +336,27 @@ * * @paramthe type of the input elements * @param type of elements accepted by downstream collector + * @param intermediate accumulation type of the downstream collector * @param result type of collector * @param mapper a function to be applied to the input elements * @param downstream a collector which will accept mapped values * @return a collector which applies the mapping function to the input * elements and provides the mapped results to the downstream collector */ - public static Collector - mapping(Function super T, ? extends U> mapper, Collector super U, R> downstream) { - BiFunction downstreamAccumulator = downstream.accumulator(); - return new CollectorImpl<>(downstream.resultSupplier(), - (r, t) -> downstreamAccumulator.apply(r, mapper.apply(t)), - downstream.combiner(), downstream.characteristics()); + public static + Collector mapping(Function super T, ? extends U> mapper, + Collector super U, A, R> downstream) { + BiConsumer downstreamAccumulator = downstream.accumulator(); + return new CollectorImpl<>(downstream.supplier(), + (r, t) -> downstreamAccumulator.accept(r, mapper.apply(t)), + downstream.combiner(), downstream.finisher(), + downstream.characteristics()); } /** - * Returns a {@code Collector } that counts the number of input - * elements. + * Returns a {@code Collector} accepting elements of type {@code T} that + * counts the number of input elements. If no elements are present, the + * result is 0. * * @implSpec * This produces a result equivalent to: @@ -391,14 +367,14 @@ * @param the type of the input elements * @return a {@code Collector} that counts the input elements */ - public static Collector + public static Collector counting() { return reducing(0L, e -> 1L, Long::sum); } /** - * Returns a {@code Collector } that produces the minimal element - * according to a given {@code Comparator}. + * Returns a {@code Collector} that produces the minimal element according + * to a given {@code Comparator}, described as an {@code Optional }. * * @implSpec * This produces a result equivalent to: @@ -410,14 +386,14 @@ * @param comparator a {@code Comparator} for comparing elements * @return a {@code Collector} that produces the minimal value */ - public static Collector + public static Collector > minBy(Comparator super T> comparator) { return reducing(BinaryOperator.minBy(comparator)); } /** - * Returns a {@code Collector } that produces the maximal element - * according to a given {@code Comparator}. + * Returns a {@code Collector} that produces the maximal element according + * to a given {@code Comparator}, described as an {@code Optional }. * * @implSpec * This produces a result equivalent to: @@ -429,39 +405,143 @@ * @param comparator a {@code Comparator} for comparing elements * @return a {@code Collector} that produces the maximal value */ - public static Collector + public static Collector > maxBy(Comparator super T> comparator) { return reducing(BinaryOperator.maxBy(comparator)); } /** - * Returns a {@code Collector } that produces the sum of a - * long-valued function applied to the input element. + * Returns a {@code Collector} that produces the sum of a integer-valued + * function applied to the input elements. If no elements are present, + * the result is 0. * - * @implSpec - * This produces a result equivalent to: - * {@code - * reducing(0L, mapper, Long::sum) - * }+ * @paramthe type of the input elements + * @param mapper a function extracting the property to be summed + * @return a {@code Collector} that produces the sum of a derived property + */ + public static Collector + summingInt(ToIntFunction super T> mapper) { + return new CollectorImpl ( + () -> new int[1], + (a, t) -> { a[0] += mapper.applyAsInt(t); }, + (a, b) -> { a[0] += b[0]; return a; }, + a -> a[0], CH_NOID); + } + + /** + * Returns a {@code Collector} that produces the sum of a long-valued + * function applied to the input elements. If no elements are present, + * the result is 0. + * + * @param the type of the input elements + * @param mapper a function extracting the property to be summed + * @return a {@code Collector} that produces the sum of a derived property + */ + public static Collector + summingLong(ToLongFunction super T> mapper) { + return new CollectorImpl ( + () -> new long[1], + (a, t) -> { a[0] += mapper.applyAsLong(t); }, + (a, b) -> { a[0] += b[0]; return a; }, + a -> a[0], CH_NOID); + } + + /** + * Returns a {@code Collector} that produces the sum of a double-valued + * function applied to the input elements. If no elements are present, + * the result is 0. + * + * The sum returned can vary depending upon the order in which + * values are recorded, due to accumulated rounding error in + * addition of values of differing magnitudes. Values sorted by increasing + * absolute magnitude tend to yield more accurate results. If any recorded + * value is a {@code NaN} or the sum is at any point a {@code NaN} then the + * sum will be {@code NaN}. + * + * @param
the type of the input elements + * @param mapper a function extracting the property to be summed + * @return a {@code Collector} that produces the sum of a derived property + */ + public static Collector + summingDouble(ToDoubleFunction super T> mapper) { + return new CollectorImpl ( + () -> new double[1], + (a, t) -> { a[0] += mapper.applyAsDouble(t); }, + (a, b) -> { a[0] += b[0]; return a; }, + a -> a[0], CH_NOID); + } + + /** + * Returns a {@code Collector} that produces the average of an integer-valued + * function applied to the input elements. If no elements are present, + * the result is 0. * * @param the type of the input elements * @param mapper a function extracting the property to be summed * @return a {@code Collector} that produces the sum of a derived property */ - public static Collector - sumBy(Function super T, Long> mapper) { - return reducing(0L, mapper, Long::sum); + public static Collector + averagingInt(ToIntFunction super T> mapper) { + return new CollectorImpl ( + () -> new long[2], + (a, t) -> { a[0] += mapper.applyAsInt(t); a[1]++; }, + (a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; }, + a -> (a[1] == 0) ? 0.0d : (double) a[0] / a[1], CH_NOID); } /** - * Returns a {@code Collector } which performs a reduction of its - * input elements under a specified {@code BinaryOperator}. + * Returns a {@code Collector} that produces the average of a long-valued + * function applied to the input elements. If no elements are present, + * the result is 0. + * + * @param the type of the input elements + * @param mapper a function extracting the property to be summed + * @return a {@code Collector} that produces the sum of a derived property + */ + public static Collector + averagingLong(ToLongFunction super T> mapper) { + return new CollectorImpl ( + () -> new long[2], + (a, t) -> { a[0] += mapper.applyAsLong(t); a[1]++; }, + (a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; }, + a -> (a[1] == 0) ? 0.0d : (double) a[0] / a[1], CH_NOID); + } + + /** + * Returns a {@code Collector} that produces the average of a double-valued + * function applied to the input elements. If no elements are present, + * the result is 0. + * + * The average returned can vary depending upon the order in which + * values are recorded, due to accumulated rounding error in + * addition of values of differing magnitudes. Values sorted by increasing + * absolute magnitude tend to yield more accurate results. If any recorded + * value is a {@code NaN} or the sum is at any point a {@code NaN} then the + * average will be {@code NaN}. + * + * @param
the type of the input elements + * @param mapper a function extracting the property to be summed + * @return a {@code Collector} that produces the sum of a derived property + */ + public static Collector + averagingDouble(ToDoubleFunction super T> mapper) { + return new CollectorImpl ( + () -> new double[2], + (a, t) -> { a[0] += mapper.applyAsDouble(t); a[1]++; }, + (a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; }, + a -> (a[1] == 0) ? 0.0d : a[0] / a[1], CH_NOID); + } + + /** + * Returns a {@code Collector} which performs a reduction of its + * input elements under a specified {@code BinaryOperator} using the + * provided identity. * * @apiNote * The {@code reducing()} collectors are most useful when used in a * multi-level reduction, downstream of {@code groupingBy} or * {@code partitioningBy}. To perform a simple reduction on a stream, - * use {@link Stream#reduce(BinaryOperator)} instead. + * use {@link Stream#reduce(Object, BinaryOperator)}} instead. * * @param element type for the input and output of the reduction * @param identity the identity value for the reduction (also, the value @@ -472,14 +552,25 @@ * @see #reducing(BinaryOperator) * @see #reducing(Object, Function, BinaryOperator) */ - public static Collector + public static Collector reducing(T identity, BinaryOperator op) { - return new CollectorImpl<>(() -> identity, (r, t) -> (r == null ? t : op.apply(r, t)), op); + return new CollectorImpl<>( + boxSupplier(identity), + (a, t) -> { a[0] = op.apply(a[0], t); }, + (a, b) -> { a[0] = op.apply(a[0], b[0]); return a; }, + a -> a[0], + CH_NOID); + } + + @SuppressWarnings("unchecked") + private static Supplier boxSupplier(T identity) { + return () -> (T[]) new Object[] { identity }; } /** - * Returns a {@code Collector } which performs a reduction of its - * input elements under a specified {@code BinaryOperator}. + * Returns a {@code Collector} which performs a reduction of its + * input elements under a specified {@code BinaryOperator}. The result + * is described as an {@code Optional }. * * @apiNote * The {@code reducing()} collectors are most useful when used in a @@ -491,15 +582,8 @@ * person in each city: * {@code * Comparator- * - * @implSpec - * The default implementation is equivalent to: - *byHeight = Comparator.comparing(Person::getHeight); - * BinaryOperator tallerOf = BinaryOperator.greaterOf(byHeight); * Map tallestByCity - * = people.stream().collect(groupingBy(Person::getCity, reducing(tallerOf))); - * } {@code - * reducing(null, op); + * = people.stream().collect(groupingBy(Person::getCity, reducing(BinaryOperator.maxBy(byHeight)))); * }* * @paramelement type for the input and output of the reduction @@ -509,13 +593,32 @@ * @see #reducing(Object, BinaryOperator) * @see #reducing(Object, Function, BinaryOperator) */ - public static Collector + public static Collector > reducing(BinaryOperator op) { - return reducing(null, op); + class OptionalBox implements Consumer { + T value = null; + boolean present = false; + + @Override + public void accept(T t) { + if (present) { + value = op.apply(value, t); + } + else { + value = t; + present = true; + } + } + } + + return new CollectorImpl >( + OptionalBox::new, OptionalBox::accept, + (a, b) -> { if (b.present) a.accept(b.value); return a; }, + a -> Optional.ofNullable(a.value), CH_NOID); } /** - * Returns a {@code Collector } which performs a reduction of its + * Returns a {@code Collector} which performs a reduction of its * input elements under a specified mapping function and * {@code BinaryOperator}. This is a generalization of * {@link #reducing(Object, BinaryOperator)} which allows a transformation @@ -524,17 +627,17 @@ * @apiNote * The {@code reducing()} collectors are most useful when used in a * multi-level reduction, downstream of {@code groupingBy} or - * {@code partitioningBy}. To perform a simple reduction on a stream, - * use {@link Stream#reduce(BinaryOperator)} instead. + * {@code partitioningBy}. To perform a simple map-reduce on a stream, + * use {@link Stream#map(Function)} and {@link Stream#reduce(Object, BinaryOperator)} + * instead. * * For example, given a stream of {@code Person}, to calculate the longest * last name of residents in each city: *
{@code * Comparator* * @parambyLength = Comparator.comparing(String::length); - * BinaryOperator longerOf = BinaryOperator.greaterOf(byLength); * Map longestLastNameByCity * = people.stream().collect(groupingBy(Person::getCity, - * reducing(Person::getLastName, longerOf))); + * reducing(Person::getLastName, BinaryOperator.maxBy(byLength)))); * } the type of the input elements @@ -549,18 +652,20 @@ * @see #reducing(BinaryOperator) */ public static - Collector reducing(U identity, - Function super T, ? extends U> mapper, - BinaryOperator op) { - return new CollectorImpl<>(() -> identity, - (r, t) -> (r == null ? mapper.apply(t) : op.apply(r, mapper.apply(t))), - op); + Collector reducing(U identity, + Function super T, ? extends U> mapper, + BinaryOperator op) { + return new CollectorImpl<>( + boxSupplier(identity), + (a, t) -> { a[0] = op.apply(a[0], mapper.apply(t)); }, + (a, b) -> { a[0] = op.apply(a[0], b[0]); return a; }, + a -> a[0], CH_NOID); } /** * Returns a {@code Collector} implementing a "group by" operation on * input elements of type {@code T}, grouping elements according to a - * classification function. + * classification function, and returning the results in a {@code Map}. * * The classification function maps elements to some key type {@code K}. * The collector produces a {@code Map
>} whose keys are the @@ -586,9 +691,9 @@ * @see #groupingBy(Function, Supplier, Collector) * @see #groupingByConcurrent(Function) */ - public static - Collector >> groupingBy(Function super T, ? extends K> classifier) { - return groupingBy(classifier, HashMap::new, toList()); + public static Collector >> + groupingBy(Function super T, ? extends K> classifier) { + return groupingBy(classifier, toList()); } /** @@ -615,6 +720,7 @@ * * @param the type of the input elements * @param the type of the keys + * @param the intermediate accumulation type of the downstream collector * @param the result type of the downstream reduction * @param classifier a classifier function mapping input elements to keys * @param downstream a {@code Collector} implementing the downstream reduction @@ -624,9 +730,9 @@ * @see #groupingBy(Function, Supplier, Collector) * @see #groupingByConcurrent(Function, Collector) */ - public static - Collector > groupingBy(Function super T, ? extends K> classifier, - Collector super T, D> downstream) { + public static + Collector > groupingBy(Function super T, ? extends K> classifier, + Collector super T, A, D> downstream) { return groupingBy(classifier, HashMap::new, downstream); } @@ -653,6 +759,7 @@ * * @param the type of the input elements * @param the type of the keys + * @param the intermediate accumulation type of the downstream collector * @param the result type of the downstream reduction * @param the type of the resulting {@code Map} * @param classifier a classifier function mapping input elements to keys @@ -665,25 +772,39 @@ * @see #groupingBy(Function) * @see #groupingByConcurrent(Function, Supplier, Collector) */ - public static > - Collector groupingBy(Function super T, ? extends K> classifier, - Supplier mapFactory, - Collector super T, D> downstream) { - Supplier downstreamSupplier = downstream.resultSupplier(); - BiFunction downstreamAccumulator = downstream.accumulator(); - BiFunction accumulator = (m, t) -> { + public static > + Collector groupingBy(Function super T, ? extends K> classifier, + Supplier mapFactory, + Collector super T, A, D> downstream) { + Supplier downstreamSupplier = downstream.supplier(); + BiConsumer downstreamAccumulator = downstream.accumulator(); + BiConsumer