public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(
            Function<? super T, ? extends K> classifier, Set<K> domain, Supplier<M> mapFactory,
            Collector<? super T, A, D> downstream) {
        Supplier<A> downstreamSupplier = downstream.supplier();
        Collector<T, ?, M> groupingBy;
        Function<K, A> supplier = k -> {
            if (!domain.contains(k))
                throw new IllegalStateException("Classifier returned value '" + k + "' which is out of domain");
            return downstreamSupplier.get();
        };
        BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
        BiConsumer<Map<K, A>, T> accumulator = (m, t) -> {
            K key = Objects.requireNonNull(classifier.apply(t));
            A container = m.computeIfAbsent(key, supplier);
            downstreamAccumulator.accept(container, t);
        };
        PartialCollector<Map<K, A>, M> partial = PartialCollector.grouping(mapFactory, downstream);
        Predicate<A> downstreamFinished = finished(downstream);
        if (downstreamFinished != null) {
            int size = domain.size();
            groupingBy = partial.asCancellable(accumulator, map -> {
                if (map.size() < size)
                    return false;
                for (A container : map.values()) {
                    if (!downstreamFinished.test(container))
                        return false;
                }
                return true;
            });
        } else {
            groupingBy = partial.asRef(accumulator);
        }
        return collectingAndThen(groupingBy, map -> {
            Function<A, D> finisher = downstream.finisher();
            domain.forEach(key -> map.computeIfAbsent(key, k -> finisher.apply(downstreamSupplier.get())));
            return map;
        });
    }
public static < T , K , D , A , M extends Map < K , D > > Collector < T , ? , M > groupingBy ( Function < ? super T , ? extends K > classifier , Set < K > domain , Supplier < M > mapFactory , Collector < ? super T , A , D > downstream ) { Supplier < A > downstreamSupplier = downstream . supplier ( ) ; Collector < T , ? , M > groupingBy ; Function < K , A > supplier = k -> { if ( ! domain . contains ( k ) ) throw new IllegalStateException ( "Classifier returned value '" + k + "' which is out of domain" ) ; return downstreamSupplier . get ( ) ; } ; BiConsumer < A , ? super T > downstreamAccumulator = downstream . accumulator ( ) ; BiConsumer < Map < K , A > , T > accumulator = ( m , t ) -> { K key = Objects . requireNonNull ( classifier . apply ( t ) ) ; A container = m . computeIfAbsent ( key , supplier ) ; downstreamAccumulator . accept ( container , t ) ; } ; PartialCollector < Map < K , A > , M > partial = PartialCollector . grouping ( mapFactory , downstream ) ; Predicate < A > downstreamFinished = finished ( downstream ) ; if ( downstreamFinished != null ) { int size = domain . size ( ) ; groupingBy = partial . asCancellable ( accumulator , map -> { if ( map . size ( ) < size ) return false ; for ( A container : map . values ( ) ) { if ( ! downstreamFinished . test ( container ) ) return false ; } return true ; } ) ; } else { groupingBy = partial . asRef ( accumulator ) ; } return collectingAndThen ( groupingBy , map -> { Function < A , D > finisher = downstream . finisher ( ) ; domain . forEach ( key -> map . computeIfAbsent ( key , k -> finisher . apply ( downstreamSupplier . get ( ) ) ) ) ; return map ; } ) ; }
Returns a {@code Collector} implementing a cascaded "group by" operation
on input elements of type {@code T}, grouping elements according to a
classification function, and then performing a reduction operation on the
values associated with a given key using the specified downstream
{@code Collector}. The {@code Map} produced by the Collector is created
with the supplied factory function.

<p>
The main difference of this collector from
{@link Collectors#groupingBy(Function, Supplier, Collector)} is that it
accepts additional domain parameter which is the {@code Set} of all
possible map keys. If the mapper function produces the key out of domain,
an {@code IllegalStateException} will occur. If the mapper function does
not produce some of domain keys at all, they are also added to the
result. These keys are mapped to the default collector value which is
equivalent to collecting an empty stream with the same collector.

<p>
This method returns a
<a href="package-summary.html#ShortCircuitReduction">short-circuiting
collector</a> if the downstream collector is short-circuiting. The
collection might stop when for every possible key from the domain the
downstream collection is known to be finished.

@param <T> the type of the input elements
@param <K> the type of the keys
@param <A> the intermediate accumulation type of the downstream collector
@param <D> the result type of the downstream reduction
@param <M> the type of the resulting {@code Map}
@param classifier a classifier function mapping input elements to keys
@param domain a domain of all possible key values
@param downstream a {@code Collector} implementing the downstream
reduction
@param mapFactory a function which, when called, produces a new empty
{@code Map} of the desired type
@return a {@code Collector} implementing the cascaded group-by operation
with given domain

@see #groupingBy(Function, Set, Collector)
@see #groupingByEnum(Class, Function, Collector)
@since 0.4.0