blob: d19f64cae2ba1326b78ea286662bb4551be51da8 [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
*
* 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 java.util.stream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.DoubleSummaryStatistics;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IntSummaryStatistics;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.LongSummaryStatistics;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
/**
* See <a
* href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html">the
* official Java API doc</a> for details.
*/
public final class Collectors {
public static <T> Collector<T,?,Double> averagingDouble(ToDoubleFunction<? super T> mapper) {
// TODO simplify to only collect average if possible
return collectingAndThen(summarizingDouble(mapper), DoubleSummaryStatistics::getAverage);
}
public static <T> Collector<T,?,Double> averagingInt(ToIntFunction<? super T> mapper) {
// TODO simplify to only collect average if possible
return collectingAndThen(summarizingInt(mapper), IntSummaryStatistics::getAverage);
}
public static <T> Collector<T,?,Double> averagingLong(ToLongFunction<? super T> mapper) {
// TODO simplify to only collect average if possible
return collectingAndThen(summarizingLong(mapper), LongSummaryStatistics::getAverage);
}
public static <T, A, R, RR> Collector<T, A, RR> collectingAndThen(
Collector<T, A, R> downstream, Function<R, RR> finisher) {
return new CollectorImpl<>(
downstream.supplier(),
downstream.accumulator(),
downstream.combiner(),
downstream.finisher().andThen(finisher));
}
public static <T> Collector<T,?,Long> counting() {
// Using Long::sum here fails in JDT
return reducing(0L, item -> 1L, (a, b) -> (Long) a.longValue() + b.longValue());
}
public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(
Function<? super T, ? extends K> classifier) {
// TODO inline this and avoid the finisher extra work of copying from a map to another map
// kept separate for now to unify implementations and reduce testing required
return groupingBy(classifier, toList());
}
public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(
Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) {
return groupingBy(classifier, HashMap::new, downstream);
}
public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(
Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream) {
return groupingBy0(() -> {
// cannot use LinkedHashMap::new because javac cannot infer correct
// return type of method reference
return new LinkedHashMap<>();
}, classifier, mapFactory, downstream);
}
private static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy0(
Supplier<Map<K, List<T>>> supplier,
Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream) {
return Collector.of(
supplier,
(m, o) -> {
K k = classifier.apply(o);
List<T> l = m.get(k);
if (l == null) {
l = new ArrayList<>();
m.put(k, l);
}
l.add(o);
},
(m1, m2) -> mergeAll(m1, m2, Collectors::addAll),
m -> {
M result = mapFactory.get();
for (Map.Entry<K, List<T>> entry : m.entrySet()) {
result.put(entry.getKey(), streamAndCollect(downstream, entry.getValue()));
}
return result;
});
}
// not supported
// public static <T,K> Collector<T,?,ConcurrentMap<K,List<T>>> groupingByConcurrent(
// Function<? super T,? extends K> classifier)
// public static <T,K,A,D> Collector<T,?,ConcurrentMap<K,D>> groupingByConcurrent(
// Function<? super T,? extends K> classifier, Collector<? super T,A,D> downstream)
// public static <T,K,A,D,M extends ConcurrentMap<K,D>> Collector<T,?,M> groupingByConcurrent(
// Function<? super T,? extends K> classifier, Supplier<M> mapFactory,
// Collector<? super T,A,D> downstream)
public static Collector<CharSequence,?,String> joining() {
// specific implementation rather than calling joining("") since we don't need to worry about
// appending delimiters between empty strings
return Collector.of(
StringBuilder::new,
StringBuilder::append,
StringBuilder::append,
StringBuilder::toString
);
}
public static Collector<CharSequence,?,String> joining(CharSequence delimiter) {
return joining(delimiter, "", "");
}
public static Collector<CharSequence, ?, String> joining(
final CharSequence delimiter, CharSequence prefix, CharSequence suffix) {
return Collector.of(
() -> new StringJoiner(delimiter, prefix, suffix),
StringJoiner::add,
StringJoiner::merge,
StringJoiner::toString
);
}
public static <T, U, A, R> Collector<T, ?, R> mapping(
final Function<? super T, ? extends U> mapper, final Collector<? super U, A, R> downstream) {
return new CollectorImpl<>(
downstream.supplier(),
(BiConsumer<A, T>)
(A a, T t) -> {
downstream.accumulator().accept(a, mapper.apply(t));
},
downstream.combiner(),
downstream.finisher());
}
public static <T> Collector<T,?,Optional<T>> maxBy(Comparator<? super T> comparator) {
return reducing(BinaryOperator.maxBy(comparator));
}
public static <T> Collector<T,?,Optional<T>> minBy(final Comparator<? super T> comparator) {
return reducing(BinaryOperator.minBy(comparator));
}
public static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(
Predicate<? super T> predicate) {
return partitioningBy(predicate, toList());
}
public static <T, D, A> Collector<T, ?, Map<Boolean, D>> partitioningBy(
Predicate<? super T> predicate, Collector<? super T, A, D> downstream) {
return groupingBy0(partitionSupplier(), predicate::test, HashMap::new, downstream);
}
private static <T> Supplier<Map<Boolean, List<T>>> partitionSupplier() {
return () -> {
Map<Boolean, List<T>> partition = new LinkedHashMap<>();
partition.put(false, new ArrayList<>());
partition.put(true, new ArrayList<>());
return partition;
};
}
public static <T> Collector<T,?,Optional<T>> reducing(BinaryOperator<T> op) {
return reducing(Optional.empty(), Optional::of, (a, b) -> {
if (!a.isPresent()) {
return b;
}
if (!b.isPresent()) {
return a;
}
return Optional.of(op.apply(a.get(), b.get()));
});
}
public static <T> Collector<T,?,T> reducing(T identity, BinaryOperator<T> op) {
return reducing(identity, Function.identity(), op);
}
@SuppressWarnings("unchecked")
public static <T, U> Collector<T, ?, U> reducing(
final U identity, final Function<? super T, ? extends U> mapper, BinaryOperator<U> op) {
return Collector.of(
() -> new Object[]{identity},
(u, t) -> u[0] = op.apply((U) u[0], mapper.apply(t)),
(Object[] u1, Object[] u2) -> {
u1[0] = op.apply((U) u1[0], (U) u2[0]);
return u1;
},
(Object[] a) -> (U) a[0]
);
}
public static <T> Collector<T, ?, DoubleSummaryStatistics> summarizingDouble(
ToDoubleFunction<? super T> mapper) {
return Collector.of(
DoubleSummaryStatistics::new,
(stats, item) -> stats.accept(mapper.applyAsDouble(item)),
(t, u) -> {
t.combine(u);
return t;
},
Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH
);
}
public static <T> Collector<T, ?, IntSummaryStatistics> summarizingInt(
ToIntFunction<? super T> mapper) {
return Collector.of(
IntSummaryStatistics::new,
(stats, item) -> stats.accept(mapper.applyAsInt(item)),
(t, u) -> {
t.combine(u);
return t;
},
Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH
);
}
public static <T> Collector<T, ?, LongSummaryStatistics> summarizingLong(
ToLongFunction<? super T> mapper) {
return Collector.of(
LongSummaryStatistics::new,
(stats, item) -> stats.accept(mapper.applyAsLong(item)),
(t, u) -> {
t.combine(u);
return t;
},
Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH
);
}
public static <T> Collector<T,?,Double> summingDouble(final ToDoubleFunction<? super T> mapper) {
// TODO simplify to only collect sum if possible
return collectingAndThen(summarizingDouble(mapper), DoubleSummaryStatistics::getSum);
}
public static <T> Collector<T,?,Integer> summingInt(ToIntFunction<? super T> mapper) {
// TODO simplify to only collect sum if possible
return collectingAndThen(
summarizingInt(mapper), intSummaryStatistics -> (int) intSummaryStatistics.getSum());
}
public static <T> Collector<T,?,Long> summingLong(ToLongFunction<? super T> mapper) {
// TODO simplify to only collect sum if possible
return collectingAndThen(summarizingLong(mapper), LongSummaryStatistics::getSum);
}
public static <T, C extends Collection<T>> Collector<T, ?, C> toCollection(
final Supplier<C> collectionFactory) {
return Collector.of(
collectionFactory,
Collection::add,
// TODO switch to a lambda reference once #9333 is fixed
(c1, c2) -> addAll(c1, c2),
Collector.Characteristics.IDENTITY_FINISH
);
}
// not supported
// public static <T,K,U> Collector<T,?,ConcurrentMap<K,U>> toConcurrentMap(
// Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper)
// public static <T,K,U> Collector<T,?,ConcurrentMap<K,U>> toConcurrentMap(
// Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper,
// BinaryOperator<U> mergeFunction)
// public static <T,K,U,M extends ConcurrentMap<K,U>> Collector<T,?,M> toConcurrentMap(
// Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper,
// BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)
public static <T> Collector<T,?,List<T>> toList() {
return toCollection(ArrayList::new);
}
public static <T, K, U> Collector<T, ?, Map<K, U>> toMap(
final Function<? super T, ? extends K> keyMapper,
final Function<? super T, ? extends U> valueMapper) {
return toMap(
keyMapper,
valueMapper,
(m1, m2) -> {
throw new IllegalStateException("Can't assign multiple values to the same key");
});
}
public static <T, K, U> Collector<T, ?, Map<K, U>> toMap(
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction) {
return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}
public static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(
final Function<? super T, ? extends K> keyMapper,
final Function<? super T, ? extends U> valueMapper,
final BinaryOperator<U> mergeFunction,
final Supplier<M> mapSupplier) {
return Collector.of(
mapSupplier,
(map, item) -> {
K key = keyMapper.apply(item);
U newValue = valueMapper.apply(item);
if (map.containsKey(key)) {
map.put(key, mergeFunction.apply(map.get(key), newValue));
} else {
map.put(key, newValue);
}
},
(m1, m2) -> mergeAll(m1, m2, mergeFunction),
Collector.Characteristics.IDENTITY_FINISH);
}
public static <T> Collector<T,?,Set<T>> toSet() {
return Collector.<T, HashSet<T>, Set<T>>of(
HashSet::new,
HashSet::add,
// TODO switch to a lambda reference once #9333 is fixed
(c1, c2) -> addAll(c1, c2),
// this is Function.identity, but Java doesn't like it here to change types.
s -> s,
Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH
);
}
private static <T, D, A> D streamAndCollect(Collector<? super T, A, D> downstream, List<T> list) {
A a = downstream.supplier().get();
for (T t : list) {
downstream.accumulator().accept(a, t);
}
return downstream.finisher().apply(a);
}
private static <K, V, M extends Map<K, V>> M mergeAll(
M m1, M m2, BinaryOperator<V> mergeFunction) {
for (Map.Entry<K, V> entry : m2.entrySet()) {
m1.merge(entry.getKey(), entry.getValue(), mergeFunction);
}
return m1;
}
private static <T, C extends Collection<T>> C addAll(C collection, Collection<T> items) {
collection.addAll(items);
return collection;
}
private Collectors() { }
}