All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.annimon.stream.Collectors Maven / Gradle / Ivy

/** Eclipse Class Decompiler plugin, Copyright (c) 2017 Chen Chao. */
/*
 * Copyright (C) 2017 HaiYang Li
 *
 * 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 com.annimon.stream;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.annimon.stream.function.BiConsumer;
import com.annimon.stream.function.BinaryOperator;
import com.annimon.stream.function.Consumer;
import com.annimon.stream.function.Function;
import com.annimon.stream.function.Predicate;
import com.annimon.stream.function.Supplier;
import com.annimon.stream.function.ToDoubleFunction;
import com.annimon.stream.function.ToIntFunction;
import com.annimon.stream.function.ToLongFunction;

/**
 * Common implementations of {@code Collector} interface.
 * 
 * @see Collector
 */
public final class Collectors {

    private static final Supplier LONG_2ELEMENTS_ARRAY_SUPPLIER = new Supplier() {
        @Override
        public long[] get() {
            return new long[] { 0L, 0L };
        }
    };

    private static final Supplier DOUBLE_2ELEMENTS_ARRAY_SUPPLIER = new Supplier() {
        @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  the type of the input elements
     * @param  the type of the resulting collection
     * @param collectionSupplier  a supplier function that provides new collection
     * @return a {@code Collector}
     */
    public static > Collector toCollection(Supplier collectionSupplier) {
        return new CollectorsImpl<>(

                collectionSupplier,

                new BiConsumer() {
                    @Override
                    public void accept(R t, T u) {
                        t.add(u);
                    }
                });
    }

    /**
     * Returns a {@code Collector} that fills new {@code List} with input elements.
     * 
     * @param  the type of the input elements
     * @return a {@code Collector}
     */
    public static  Collector> toList() {
        return new CollectorsImpl<>(

                new Supplier>() {
                    @Override
                    public List get() {
                        return new ArrayList<>();
                    }
                },

                new BiConsumer, T>() {
                    @Override
                    public void accept(List t, T u) {
                        t.add(u);
                    }
                });
    }

    /**
     * Returns a {@code Collector} that fills new {@code Set} with input elements.
     * 
     * @param  the type of the input elements
     * @return a {@code Collector}
     */
    public static  Collector> toSet() {
        return new CollectorsImpl<>(

                new Supplier>() {
                    @Override
                    public Set get() {
                        return new HashSet<>();
                    }
                },

                new BiConsumer, T>() {
                    @Override
                    public void accept(Set t, T u) {
                        t.add(u);
                    }
                });
    }

    /**
     * Returns a {@code Collector} that fills new {@code Map} with input elements.
     *
     * @param  the type of the input elements and the result type of value mapping function
     * @param  the result type of key mapping function
     * @param keyMapper  a mapping function to produce keys
     * @return a {@code Collector}
     * @see #toMap(Function, Function, BinaryOperator, Supplier)
     * @since 1.1.3
     */
    public static  Collector> toMap(final Function keyMapper) {
        return toMap(keyMapper, Fn. identity());
    }

    /**
     * Returns a {@code Collector} that fills new {@code Map} with input elements.
     *
     * @param  the type of the input elements
     * @param  the result type of key mapping function
     * @param  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}
     * @see #toMap(Function, Function, BinaryOperator, Supplier)
     */
    public static  Collector> toMap(final Function keyMapper,
            final Function valueMapper) {
        return toMap(keyMapper, valueMapper, Fn. throwingMerger());
    }

    /**
     * 
     * @param keyMapper
     * @param valueMapper
     * @param mergeFunction
     * @return
     * @see #toMap(Function, Function, BinaryOperator, Supplier)
     */
    public static  Collector> toMap(final Function keyMapper,
            final Function valueMapper, BinaryOperator mergeFunction) {
        return toMap(keyMapper, valueMapper, mergeFunction, Fn.Suppliers. ofMap());
    }

    /**
     * 
     * @param keyMapper
     * @param valueMapper
     * @param mapFactory
     * @return
     * @see #toMap(Function, Function, BinaryOperator, Supplier)
     */
    public static > Collector toMap(final Function keyMapper,
            final Function valueMapper, final Supplier mapFactory) {
        return toMap(keyMapper, valueMapper, Fn. throwingMerger(), mapFactory);
    }

    /**
     * Returns a {@code Collector} that fills new {@code Map} with input elements.
     * 
     * @param  the type of the input elements
     * @param  the result type of key mapping function
     * @param  the result type of value mapping function
     * @param  the type of the resulting {@code Map}
     * @param keyMapper  a mapping function to produce keys
     * @param valueMapper a mapping function to produce values
     * @param mergeFunction
     * @param mapFactory  a supplier function that provides new {@code Map}
     * @return a {@code Collector}
     */
    public static > Collector toMap(final Function keyMapper,
            final Function valueMapper, final BinaryOperator mergeFunction, final Supplier mapFactory) {
        return new CollectorsImpl<>(mapFactory, new BiConsumer() {
            @Override
            public void accept(M map, T t) {
                final K key = keyMapper.apply(t);
                final V value = valueMapper.apply(t);

                if (map.containsKey(key)) {
                    map.put(key, mergeFunction.apply(map.get(key), value));
                } else {
                    map.put(key, value);
                }
            }
        });
    }

    /**
     * Returns a {@code Collector} that concatenates input elements into new string.
     * 
     * @return a {@code Collector}
     */
    public static Collector 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 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 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 joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix,
            final String emptyValue) {
        return new CollectorsImpl<>(

                new Supplier() {
                    @Override
                    public StringBuilder get() {
                        return new StringBuilder();
                    }
                },

                new BiConsumer() {
                    @Override
                    public void accept(StringBuilder t, CharSequence u) {
                        if (t.length() > 0) {
                            t.append(delimiter);
                        } else {
                            t.append(prefix);
                        }
                        t.append(u);
                    }
                },

                new Function() {
                    @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 integer-valued input elements.
     *
     * @param  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  Collector averagingInt(final ToIntFunction mapper) {
        return averagingHelper(new BiConsumer() {
            @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  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  Collector averagingLong(final ToLongFunction mapper) {
        return averagingHelper(new BiConsumer() {
            @Override
            public void accept(long[] t, T u) {
                t[0]++; // count
                t[1] += mapper.applyAsLong(u); // sum
            }
        });
    }

    private static  Collector averagingHelper(final BiConsumer accumulator) {
        return new CollectorsImpl<>(

                LONG_2ELEMENTS_ARRAY_SUPPLIER,

                accumulator,

                new Function() {
                    @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  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  Collector averagingDouble(final ToDoubleFunction mapper) {
        return new CollectorsImpl<>(

                DOUBLE_2ELEMENTS_ARRAY_SUPPLIER,

                new BiConsumer() {
                    @Override
                    public void accept(double[] t, T u) {
                        t[0]++; // count
                        t[1] += mapper.applyAsDouble(u); // sum
                    }
                },

                new Function() {
                    @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  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  Collector summingInt(final ToIntFunction mapper) {
        return new CollectorsImpl<>(

                new Supplier() {
                    @Override
                    public int[] get() {
                        return new int[] { 0 };
                    }
                },

                new BiConsumer() {
                    @Override
                    public void accept(int[] t, T u) {
                        t[0] += mapper.applyAsInt(u);
                    }
                },

                new Function() {
                    @Override
                    public Integer apply(int[] value) {
                        return value[0];
                    }
                });
    }

    /**
     * Returns a {@code Collector} that summing long-valued input elements.
     *
     * @param  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  Collector summingLong(final ToLongFunction mapper) {
        return new CollectorsImpl<>(

                LONG_2ELEMENTS_ARRAY_SUPPLIER,

                new BiConsumer() {
                    @Override
                    public void accept(long[] t, T u) {
                        t[0] += mapper.applyAsLong(u);
                    }
                },

                new Function() {
                    @Override
                    public Long apply(long[] value) {
                        return value[0];
                    }
                });
    }

    /**
     * Returns a {@code Collector} that summing double-valued input elements.
     *
     * @param  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  Collector summingDouble(final ToDoubleFunction mapper) {
        return new CollectorsImpl<>(

                DOUBLE_2ELEMENTS_ARRAY_SUPPLIER,

                new BiConsumer() {
                    @Override
                    public void accept(double[] t, T u) {
                        t[0] += mapper.applyAsDouble(u);
                    }
                },

                new Function() {
                    @Override
                    public Double apply(double[] value) {
                        return value[0];
                    }
                });
    }

    /**
     * Returns a {@code Collector} that counts the number of input elements.
     * 
     * @param  the type of the input elements
     * @return a {@code Collector}
     */
    public static  Collector counting() {
        return summingInt(new ToIntFunction() {

            @Override
            public int applyAsInt(T t) {
                return 1;
            }
        });
    }

    /**
     * Returns a {@code Collector} that reduces input elements.
     * 
     * @param  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  Collector reducing(final T identity, final BinaryOperator op) {
        return new CollectorsImpl<>(

                new Supplier>() {
                    @Override
                    public Tuple1 get() {
                        return new Tuple1<>(identity);
                    }
                },

                new BiConsumer, T>() {
                    @Override
                    public void accept(Tuple1 tuple, T value) {
                        tuple.a = op.apply(tuple.a, value);
                    }
                },

                new Function, T>() {
                    @Override
                    public T apply(Tuple1 tuple) {
                        return tuple.a;
                    }
                });
    }

    /**
     * Returns a {@code Collector} that reduces input elements.
     * 
     * @param  the type of the input elements
     * @param  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  Collector reducing(final R identity, final Function mapper, final BinaryOperator op) {
        return new CollectorsImpl<>(

                new Supplier>() {
                    @Override
                    public Tuple1 get() {
                        return new Tuple1<>(identity);
                    }
                },

                new BiConsumer, T>() {
                    @Override
                    public void accept(Tuple1 tuple, T value) {
                        tuple.a = op.apply(tuple.a, mapper.apply(value));
                    }
                },

                new Function, R>() {
                    @Override
                    public R apply(Tuple1 tuple) {
                        return tuple.a;
                    }
                });
    }

    /**
     * Returns a {@code Collector} that filters input elements.
     *
     * @param  the type of the input elements
     * @param  the accumulation type
     * @param  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  Collector filtering(final Predicate predicate, final Collector downstream) {
        final BiConsumer accumulator = downstream.accumulator();
        return new CollectorsImpl<>(

                downstream.supplier(),

                new BiConsumer() {
                    @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  the type of the input elements
     * @param  the result type of mapping function
     * @param  the accumulation type
     * @param  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  Collector mapping(final Function mapper, final Collector downstream) {

        final BiConsumer accumulator = downstream.accumulator();
        return new CollectorsImpl<>(

                downstream.supplier(),

                new BiConsumer() {
                    @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  the type of the input elements
     * @param  the result type of flat-mapping function
     * @param  the accumulation type
     * @param  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  Collector flatMapping(final Function> mapper,
            final Collector downstream) {

        final BiConsumer accumulator = downstream.accumulator();
        return new CollectorsImpl<>(

                downstream.supplier(),

                new BiConsumer() {
                    @Override
                    public void accept(final A a, T t) {
                        final Stream stream = mapper.apply(t);
                        if (stream == null)
                            return;
                        stream.forEach(new Consumer() {
                            @Override
                            public void accept(U u) {
                                accumulator.accept(a, u);
                            }
                        });
                    }
                },

                downstream.finisher());
    }

    /**
     * Returns a {@code Collector} that performs additional transformation.
     * 
     * @param  the type of the input elements
     * @param  the accumulation type
     * @param  the input type of the transformation function
     * @param  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  Collector collectingAndThen(final Collector c, final Function finisher) {
        @SuppressWarnings({ "rawtypes", "unchecked" })
        final Function downstreamFinisher = c.finisher() == null ? (Function) castIdentity() : c.finisher();

        final Function newFinisher = new Function() {
            @Override
            public OR apply(A t) {
                return finisher.apply(downstreamFinisher.apply(t));
            }
        };

        return new CollectorsImpl<>(c.supplier(), c.accumulator(), newFinisher);
    }

    /**
     * Returns a {@code Collector} that performs grouping operation by given classifier.
     * 
     * @param  the type of the input elements
     * @param  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.Collector, com.annimon.stream.function.Supplier) 
     */
    public static  Collector>> groupingBy(Function classifier) {
        return groupingBy(classifier, Collectors. toList());
    }

    /**
     * Returns a {@code Collector} that performs grouping operation by given classifier.
     * 
     * @param  the type of the input elements
     * @param  the type of the keys
     * @param  the accumulation type
     * @param  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.Collector, com.annimon.stream.function.Supplier) 
     */
    public static  Collector> groupingBy(Function classifier, Collector downstream) {
        return Collectors.> groupingBy(classifier, downstream, Fn.Suppliers. ofMap());
    }

    /**
     * Returns a {@code Collector} that performs grouping operation by given classifier.
     * 
     * @param  the type of the input elements
     * @param  the type of the keys
     * @param  the accumulation type
     * @param  the result type of downstream reduction
     * @param  the type of the resulting {@code Map}
     * @param classifier  the classifier function 
     * @param downstream  the collector of mapped elements
     * @param mapFactory  a supplier function that provides new {@code Map}
     * @return a {@code Collector}
     * @see #groupingBy(com.annimon.stream.function.Function) 
     * @see #groupingBy(com.annimon.stream.function.Function, com.annimon.stream.Collector) 
     */
    public static > Collector groupingBy(final Function classifier,
            final Collector downstream, final Supplier mapFactory) {

        @SuppressWarnings("unchecked")
        final Function downstreamFinisher = (Function) downstream.finisher();
        Function, M> finisher = null;
        if (downstreamFinisher != null) {
            finisher = new Function, M>() {
                @Override
                public M apply(Map map) {
                    // Update values of a map by a finisher function
                    for (Map.Entry 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> castedMapFactory = (Supplier>) mapFactory;
        return new CollectorsImpl<>(castedMapFactory,

                new BiConsumer, T>() {
                    @Override
                    public void accept(Map 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);
    }

    @SuppressWarnings("unchecked")
    static  Function castIdentity() {
        return new Function() {

            @Override
            public R apply(A value) {
                return (R) value;
            }
        };
    }

    private static final class Tuple1 {
        A a;

        Tuple1(A a) {
            this.a = a;
        }
    }

    private static final class CollectorsImpl implements Collector {

        private final Supplier supplier;
        private final BiConsumer accumulator;
        private final Function finisher;

        public CollectorsImpl(Supplier supplier, BiConsumer accumulator) {
            this(supplier, accumulator, null);
        }

        public CollectorsImpl(Supplier supplier, BiConsumer accumulator, Function finisher) {
            this.supplier = supplier;
            this.accumulator = accumulator;
            this.finisher = finisher;
        }

        @Override
        public Supplier supplier() {
            return supplier;
        }

        @Override
        public BiConsumer accumulator() {
            return accumulator;
        }

        @Override
        public Function finisher() {
            return finisher;
        }

    }
}