package com.annimon.stream; import com.annimon.stream.function.*; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Common implementations of {@code Collector} interface. * * @see Collector */ public final class Collectors { private static final Supplier<long[]> LONG_2ELEMENTS_ARRAY_SUPPLIER = new Supplier<long[]>() { @Override public long[] get() { return new long[] { 0L, 0L }; } }; private static final Supplier<double[]> DOUBLE_2ELEMENTS_ARRAY_SUPPLIER = new Supplier<double[]>() { @Override public double[] get() { return new double[] { 0d, 0d }; } }; private Collectors() { } /** * Returns a {@code Collector} that fills new {@code Collection}, provided by {@code collectionSupplier}, * with input elements. * * @param <T> the type of the input elements * @param <R> the type of the resulting collection * @param collectionSupplier a supplier function that provides new collection * @return a {@code Collector} */ public static <T, R extends Collection<T>> Collector<T, ?, R> toCollection(Supplier<R> collectionSupplier) { return new CollectorsImpl<T, R, R>( collectionSupplier, new BiConsumer<R, T>() { @Override public void accept(R t, T u) { t.add(u); } } ); } /** * Returns a {@code Collector} that fills new {@code List} with input elements. * * @param <T> the type of the input elements * @return a {@code Collector} */ public static <T> Collector<T, ?, List<T>> toList() { return new CollectorsImpl<T, List<T>, List<T>>( new Supplier<List<T>>() { @Override public List<T> get() { return new ArrayList<T>(); } }, new BiConsumer<List<T>, T>() { @Override public void accept(List<T> t, T u) { t.add(u); } } ); } /** * Returns a {@code Collector} that fills new {@code Set} with input elements. * * @param <T> the type of the input elements * @return a {@code Collector} */ public static <T> Collector<T, ?, Set<T>> toSet() { return new CollectorsImpl<T, Set<T>, Set<T>>( new Supplier<Set<T>>() { @Override public Set<T> get() { return new HashSet<T>(); } }, new BiConsumer<Set<T>, T>() { @Override public void accept(Set<T> t, T u) { t.add(u); } } ); } /** * Returns a {@code Collector} that fills new {@code Map} with input elements. * * @param <T> the type of the input elements and the result type of value mapping function * @param <K> the result type of key mapping function * @param keyMapper a mapping function to produce keys * @return a {@code Collector} * @since 1.1.3 */ public static <T, K> Collector<T, ?, Map<K, T>> toMap( final Function<? super T, ? extends K> keyMapper) { return Collectors.<T, K, T>toMap(keyMapper, UnaryOperator.Util.<T>identity()); } /** * Returns a {@code Collector} that fills new {@code Map} with input elements. * * @param <T> the type of the input elements * @param <K> the result type of key mapping function * @param <V> the result type of value mapping function * @param keyMapper a mapping function to produce keys * @param valueMapper a mapping function to produce values * @return a {@code Collector} */ public static <T, K, V> Collector<T, ?, Map<K, V>> toMap( final Function<? super T, ? extends K> keyMapper, final Function<? super T, ? extends V> valueMapper) { return Collectors.<T, K, V, Map<K, V>>toMap(keyMapper, valueMapper, Collectors.<K, V>hashMapSupplier()); } /** * Returns a {@code Collector} that fills new {@code Map} with input elements. * * @param <T> the type of the input elements * @param <K> the result type of key mapping function * @param <V> the result type of value mapping function * @param <M> the type of the resulting {@code Map} * @param keyMapper a mapping function to produce keys * @param valueMapper a mapping function to produce values * @param mapFactory a supplier function that provides new {@code Map} * @return a {@code Collector} */ public static <T, K, V, M extends Map<K, V>> Collector<T, ?, M> toMap( final Function<? super T, ? extends K> keyMapper, final Function<? super T, ? extends V> valueMapper, final Supplier<M> mapFactory) { return new CollectorsImpl<T, M, M>( mapFactory, new BiConsumer<M, T>() { @Override public void accept(M map, T t) { final K key = keyMapper.apply(t); final V value = valueMapper.apply(t); final V oldValue = map.get(key); final V newValue = (oldValue == null) ? value : oldValue; if (newValue == null) { map.remove(key); } else { map.put(key, newValue); } } } ); } /** * Returns a {@code Collector} that concatenates input elements into new string. * * @return a {@code Collector} */ public static Collector<CharSequence, ?, String> joining() { return joining(""); } /** * Returns a {@code Collector} that concatenates input elements into new string. * * @param delimiter the delimiter between each element * @return a {@code Collector} */ public static Collector<CharSequence, ?, String> joining(CharSequence delimiter) { return joining(delimiter, "", ""); } /** * Returns a {@code Collector} that concatenates input elements into new string. * * @param delimiter the delimiter between each element * @param prefix the prefix of result * @param suffix the suffix of result * @return a {@code Collector} */ public static Collector<CharSequence, ?, String> joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix) { return joining(delimiter, prefix, suffix, prefix.toString() + suffix.toString()); } /** * Returns a {@code Collector} that concatenates input elements into new string. * * @param delimiter the delimiter between each element * @param prefix the prefix of result * @param suffix the suffix of result * @param emptyValue the string which replaces empty element if exists * @return a {@code Collector} */ public static Collector<CharSequence, ?, String> joining( final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix, final String emptyValue) { return new CollectorsImpl<CharSequence, StringBuilder, String>( new Supplier<StringBuilder>() { @Override public StringBuilder get() { return new StringBuilder(); } }, new BiConsumer<StringBuilder, CharSequence>() { @Override public void accept(StringBuilder t, CharSequence u) { if (t.length() > 0) { t.append(delimiter); } else { t.append(prefix); } t.append(u); } }, new Function<StringBuilder, String>() { @Override public String apply(StringBuilder value) { if (value.length() == 0) { return emptyValue; } else { value.append(suffix); return value.toString(); } } } ); } /** * Returns a {@code Collector} that calculates average of input elements. * * @param <T> the type of the input elements * @param mapper the mapping function which extracts value from element to calculate result * @deprecated As of release 1.1.3, replaced by * {@link #averagingDouble(com.annimon.stream.function.ToDoubleFunction)} * @return a {@code Collector} */ @Deprecated public static <T> Collector<T, ?, Double> averaging(final Function<? super T, Double> mapper) { return averagingDouble(new ToDoubleFunction<T>() { @Override public double applyAsDouble(T t) { return mapper.apply(t); } }); } /** * Returns a {@code Collector} that calculates average of integer-valued input elements. * * @param <T> the type of the input elements * @param mapper the mapping function which extracts value from element to calculate result * @return a {@code Collector} * @since 1.1.3 */ public static <T> Collector<T, ?, Double> averagingInt(final ToIntFunction<? super T> mapper) { return averagingHelper(new BiConsumer<long[], T>() { @Override public void accept(long[] t, T u) { t[0]++; // count t[1] += mapper.applyAsInt(u); // sum } }); } /** * Returns a {@code Collector} that calculates average of long-valued input elements. * * @param <T> the type of the input elements * @param mapper the mapping function which extracts value from element to calculate result * @return a {@code Collector} * @since 1.1.3 */ public static <T> Collector<T, ?, Double> averagingLong(final ToLongFunction<? super T> mapper) { return averagingHelper(new BiConsumer<long[], T>() { @Override public void accept(long[] t, T u) { t[0]++; // count t[1] += mapper.applyAsLong(u); // sum } }); } private static <T> Collector<T, ?, Double> averagingHelper(final BiConsumer<long[], T> accumulator) { return new CollectorsImpl<T, long[], Double>( LONG_2ELEMENTS_ARRAY_SUPPLIER, accumulator, new Function<long[], Double>() { @Override public Double apply(long[] t) { if (t[0] == 0) return 0d; return t[1] / (double) t[0]; } } ); } /** * Returns a {@code Collector} that calculates average of double-valued input elements. * * @param <T> the type of the input elements * @param mapper the mapping function which extracts value from element to calculate result * @return a {@code Collector} * @since 1.1.3 */ public static <T> Collector<T, ?, Double> averagingDouble(final ToDoubleFunction<? super T> mapper) { return new CollectorsImpl<T, double[], Double>( DOUBLE_2ELEMENTS_ARRAY_SUPPLIER, new BiConsumer<double[], T>() { @Override public void accept(double[] t, T u) { t[0]++; // count t[1] += mapper.applyAsDouble(u); // sum } }, new Function<double[], Double>() { @Override public Double apply(double[] t) { if (t[0] == 0) return 0d; return t[1] / t[0]; } } ); } /** * Returns a {@code Collector} that summing integer-valued input elements. * * @param <T> the type of the input elements * @param mapper the mapping function which extracts value from element to calculate result * @return a {@code Collector} * @since 1.1.3 */ public static <T> Collector<T, ?, Integer> summingInt(final ToIntFunction<? super T> mapper) { return new CollectorsImpl<T, int[], Integer>( new Supplier<int[]>() { @Override public int[] get() { return new int[] { 0 }; } }, new BiConsumer<int[], T>() { @Override public void accept(int[] t, T u) { t[0] += mapper.applyAsInt(u); } }, new Function<int[], Integer>() { @Override public Integer apply(int[] value) { return value[0]; } } ); } /** * Returns a {@code Collector} that summing long-valued input elements. * * @param <T> the type of the input elements * @param mapper the mapping function which extracts value from element to calculate result * @return a {@code Collector} * @since 1.1.3 */ public static <T> Collector<T, ?, Long> summingLong(final ToLongFunction<? super T> mapper) { return new CollectorsImpl<T, long[], Long>( LONG_2ELEMENTS_ARRAY_SUPPLIER, new BiConsumer<long[], T>() { @Override public void accept(long[] t, T u) { t[0] += mapper.applyAsLong(u); } }, new Function<long[], Long>() { @Override public Long apply(long[] value) { return value[0]; } } ); } /** * Returns a {@code Collector} that summing double-valued input elements. * * @param <T> the type of the input elements * @param mapper the mapping function which extracts value from element to calculate result * @return a {@code Collector} * @since 1.1.3 */ public static <T> Collector<T, ?, Double> summingDouble(final ToDoubleFunction<? super T> mapper) { return new CollectorsImpl<T, double[], Double>( DOUBLE_2ELEMENTS_ARRAY_SUPPLIER, new BiConsumer<double[], T>() { @Override public void accept(double[] t, T u) { t[0] += mapper.applyAsDouble(u); } }, new Function<double[], Double>() { @Override public Double apply(double[] value) { return value[0]; } } ); } /** * Returns a {@code Collector} that counts the number of input elements. * * @param <T> the type of the input elements * @return a {@code Collector} */ public static <T> Collector<T, ?, Long> counting() { return summingLong(new ToLongFunction<T>() { @Override public long applyAsLong(T t) { return 1L; } }); } /** * Returns a {@code Collector} that reduces input elements. * * @param <T> the type of the input elements * @param identity the initial value * @param op the operator to reduce elements * @return a {@code Collector} * @see #reducing(java.lang.Object, com.annimon.stream.function.Function, com.annimon.stream.function.BinaryOperator) */ public static <T> Collector<T, ?, T> reducing(final T identity, final BinaryOperator<T> op) { return new CollectorsImpl<T, Tuple1<T>, T>( new Supplier<Tuple1<T>>() { @Override public Tuple1<T> get() { return new Tuple1<T>(identity); } }, new BiConsumer<Tuple1<T>, T>() { @Override public void accept(Tuple1<T> tuple, T value) { tuple.a = op.apply(tuple.a, value); } }, new Function<Tuple1<T>, T>() { @Override public T apply(Tuple1<T> tuple) { return tuple.a; } } ); } /** * Returns a {@code Collector} that reduces input elements. * * @param <T> the type of the input elements * @param <R> the type of the output elements * @param identity the initial value * @param mapper the mapping function * @param op the operator to reduce elements * @return a {@code Collector} * @see #reducing(java.lang.Object, com.annimon.stream.function.BinaryOperator) */ public static <T, R> Collector<T, ?, R> reducing( final R identity, final Function<? super T, ? extends R> mapper, final BinaryOperator<R> op) { return new CollectorsImpl<T, Tuple1<R>, R>( new Supplier<Tuple1<R>>() { @Override public Tuple1<R> get() { return new Tuple1<R>(identity); } }, new BiConsumer<Tuple1<R>, T>() { @Override public void accept(Tuple1<R> tuple, T value) { tuple.a = op.apply(tuple.a, mapper.apply(value)); } }, new Function<Tuple1<R>, R>() { @Override public R apply(Tuple1<R> tuple) { return tuple.a; } } ); } /** * Returns a {@code Collector} that filters input elements. * * @param <T> the type of the input elements * @param <A> the accumulation type * @param <R> the type of the output elements * @param predicate a predicate used to filter elements * @param downstream the collector of filtered elements * @return a {@code Collector} * @since 1.1.3 */ public static <T, A, R> Collector<T, ?, R> filtering( final Predicate<? super T> predicate, final Collector<? super T, A, R> downstream) { final BiConsumer<A, ? super T> accumulator = downstream.accumulator(); return new CollectorsImpl<T, A, R>( downstream.supplier(), new BiConsumer<A, T>() { @Override public void accept(A a, T t) { if (predicate.test(t)) accumulator.accept(a, t); } }, downstream.finisher() ); } /** * Returns a {@code Collector} that performs mapping before accumulation. * * @param <T> the type of the input elements * @param <U> the result type of mapping function * @param <A> the accumulation type * @param <R> the result type of collector * @param mapper a function that performs mapping to input elements * @param downstream the collector of mapped elements * @return a {@code Collector} */ public static <T, U, A, R> Collector<T, ?, R> mapping( final Function<? super T, ? extends U> mapper, final Collector<? super U, A, R> downstream) { final BiConsumer<A, ? super U> accumulator = downstream.accumulator(); return new CollectorsImpl<T, A, R>( downstream.supplier(), new BiConsumer<A, T>() { @Override public void accept(A a, T t) { accumulator.accept(a, mapper.apply(t)); } }, downstream.finisher() ); } /** * Returns a {@code Collector} that performs flat-mapping before accumulation. * * @param <T> the type of the input elements * @param <U> the result type of flat-mapping function * @param <A> the accumulation type * @param <R> the result type of collector * @param mapper a function that performs flat-mapping to input elements * @param downstream the collector of flat-mapped elements * @return a {@code Collector} * @since 1.1.3 */ public static <T, U, A, R> Collector<T, ?, R> flatMapping( final Function<? super T, ? extends Stream<? extends U>> mapper, final Collector<? super U, A, R> downstream) { final BiConsumer<A, ? super U> accumulator = downstream.accumulator(); return new CollectorsImpl<T, A, R>( downstream.supplier(), new BiConsumer<A, T>() { @Override public void accept(final A a, T t) { final Stream<? extends U> stream = mapper.apply(t); if (stream == null) return; stream.forEach(new Consumer<U>() { @Override public void accept(U u) { accumulator.accept(a, u); } }); } }, downstream.finisher() ); } /** * Returns a {@code Collector} that performs additional transformation. * * @param <T> the type of the input elements * @param <A> the accumulation type * @param <IR> the input type of the transformation function * @param <OR> the output type of the transformation function * @param c the input {@code Collector} * @param finisher the final transformation function * @return a {@code Collector} */ public static <T, A, IR, OR> Collector<T, A, OR> collectingAndThen( Collector<T, A, IR> c, Function<IR, OR> finisher) { Function<A, IR> downstreamFinisher = c.finisher(); if (downstreamFinisher == null) { downstreamFinisher = castIdentity(); } return new CollectorsImpl<T, A, OR>(c.supplier(), c.accumulator(), Function.Util.andThen(downstreamFinisher, finisher)); } /** * Returns a {@code Collector} that performs grouping operation by given classifier. * * @param <T> the type of the input elements * @param <K> the type of the keys * @param classifier the classifier function * @return a {@code Collector} * @see #groupingBy(com.annimon.stream.function.Function, com.annimon.stream.Collector) * @see #groupingBy(com.annimon.stream.function.Function, com.annimon.stream.function.Supplier, com.annimon.stream.Collector) */ public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy( Function<? super T, ? extends K> classifier) { return groupingBy(classifier, Collectors.<T>toList()); } /** * Returns a {@code Collector} that performs grouping operation by given classifier. * * @param <T> the type of the input elements * @param <K> the type of the keys * @param <A> the accumulation type * @param <D> the result type of downstream reduction * @param classifier the classifier function * @param downstream the collector of mapped elements * @return a {@code Collector} * @see #groupingBy(com.annimon.stream.function.Function) * @see #groupingBy(com.annimon.stream.function.Function, com.annimon.stream.function.Supplier, com.annimon.stream.Collector) */ 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 Collectors.<T, K, D, A, Map<K, D>>groupingBy(classifier, Collectors.<K, D>hashMapSupplier(), downstream); } /** * Returns a {@code Collector} that performs grouping operation by given classifier. * * @param <T> the type of the input elements * @param <K> the type of the keys * @param <A> the accumulation type * @param <D> the result type of downstream reduction * @param <M> the type of the resulting {@code Map} * @param classifier the classifier function * @param mapFactory a supplier function that provides new {@code Map} * @param downstream the collector of mapped elements * @return a {@code Collector} * @see #groupingBy(com.annimon.stream.function.Function) * @see #groupingBy(com.annimon.stream.function.Function, com.annimon.stream.Collector) */ public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy( final Function<? super T, ? extends K> classifier, final Supplier<M> mapFactory, final Collector<? super T, A, D> downstream) { @SuppressWarnings("unchecked") final Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher(); Function<Map<K, A>, M> finisher = null; if (downstreamFinisher != null) { finisher = new Function<Map<K, A>, M>() { @Override public M apply(Map<K, A> map) { // Update values of a map by a finisher function for (Map.Entry<K, A> entry : map.entrySet()) { A value = entry.getValue(); value = downstreamFinisher.apply(value); entry.setValue(value); } @SuppressWarnings("unchecked") M castedMap = (M) map; return castedMap; } }; } @SuppressWarnings("unchecked") Supplier<Map<K, A>> castedMapFactory = (Supplier<Map<K, A>>) mapFactory; return new CollectorsImpl<T, Map<K, A>, M>( castedMapFactory, new BiConsumer<Map<K, A>, T>() { @Override public void accept(Map<K, A> map, T t) { K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key"); // Get container with currently grouped elements A container = map.get(key); if (container == null) { // Put new container (list, map, set, etc) container = downstream.supplier().get(); map.put(key, container); } // Add element to container downstream.accumulator().accept(container, t); } }, finisher ); } private static <K, V> Supplier<Map<K, V>> hashMapSupplier() { return new Supplier<Map<K, V>>() { @Override public Map<K, V> get() { return new HashMap<K, V>(); } }; } @SuppressWarnings("unchecked") static <A, R> Function<A, R> castIdentity() { return new Function<A, R>() { @Override public R apply(A value) { return (R) value; } }; } private static final class Tuple1<A> { A a; Tuple1(A a) { this.a = a; } } private static final class CollectorsImpl<T, A, R> implements Collector<T, A, R> { private final Supplier<A> supplier; private final BiConsumer<A, T> accumulator; private final Function<A, R> finisher; public CollectorsImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator) { this(supplier, accumulator, null); } public CollectorsImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator, Function<A, R> finisher) { this.supplier = supplier; this.accumulator = accumulator; this.finisher = finisher; } @Override public Supplier<A> supplier() { return supplier; } @Override public BiConsumer<A, T> accumulator() { return accumulator; } @Override public Function<A, R> finisher() { return finisher; } } }