import java.util.AbstractMap; import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector; import java.util.stream.Stream; import static java.util.stream.Collectors.*; final class Collectors { private Collectors() { } public static > Collector groupingByFlatKeys(Function> classifier, Supplier mapFactory, Collector downstream) { return flatMapping(t -> classifier.apply(t).map(k -> new AbstractMap.SimpleEntry<>(k, t)), groupingBy(Map.Entry::getKey, mapFactory, mapping(Map.Entry::getValue, downstream))); } public static Collector> toMapByFlatKeys(Function> keyMapper, Function valueMapper) { return flatMapping(t -> keyMapper.apply(t).map(k -> new AbstractMap.SimpleEntry<>(k, t)), toMap(Map.Entry::getKey, valueMapper.compose(Map.Entry::getValue))); } public static Collector flatMapping(Function> mapper, Collector downstream) { BiConsumer downstreamAccumulator = downstream.accumulator(); return Collector.of(downstream.supplier(), (r, t) -> mapper.apply(t).forEach(v -> downstreamAccumulator.accept(r, v)), downstream.combiner(), downstream.finisher(), downstream.characteristics().toArray(new Collector.Characteristics[downstream.characteristics().size()])); } } class SimplifiedTest { public static void a(Stream classifier) { flatMapping(classifier.map(k -> new AbstractMap.SimpleEntry(k,k.toString())), Map.Entry::getKey); } public static void flatMapping(Stream stream, Function downstream) {} }