Java 8 Stream API - Java 9 Collectors.flatMapping rewritten in Java 8

后端 未结 2 1233
一个人的身影
一个人的身影 2021-02-05 12:19

I got in touch with a new feature since java-9 called Collectors.flatMapping that takes place as a downstream of grouping or partitioning. Such as (example taken from here):

相关标签:
2条回答
  • 2021-02-05 13:11

    I would just backport flatMapping. It only requires 2 methods and 1 class, with no other dependencies.

    Also, when it comes time to upgrade to Java 9, you can just deprecate your version and replace any usages of it with the proper version.

    The following code is taken from the JDK. I didn't write it. I have tested it with your example and it returns the same result.

    class Nikollectors
    {
       public static <T, U, A, R> Collector<T, ?, R> flatMapping(Function<? super T, ? extends Stream<? extends U>> mapper, Collector<? super U, A, R> downstream) {
            BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator();
            return new CollectorImpl<>(downstream.supplier(),
                (r, t) -> {
                    try (Stream<? extends U> result = mapper.apply(t)) {
                        if (result != null)
                            result.sequential().forEach(u -> downstreamAccumulator.accept(r, u));
                    }
                },
                downstream.combiner(), downstream.finisher(),
                downstream.characteristics());
        }
    
       private static class CollectorImpl<T, A, R> implements Collector<T, A, R>
       {
            private final Supplier<A> supplier;
            private final BiConsumer<A, T> accumulator;
            private final BinaryOperator<A> combiner;
            private final Function<A, R> finisher;
            private final Set<Characteristics> characteristics;
    
            CollectorImpl(Supplier<A> supplier,
                          BiConsumer<A, T> accumulator,
                          BinaryOperator<A> combiner,
                          Function<A,R> finisher,
                          Set<Characteristics> characteristics) {
                this.supplier = supplier;
                this.accumulator = accumulator;
                this.combiner = combiner;
                this.finisher = finisher;
                this.characteristics = characteristics;
            }
    
            CollectorImpl(Supplier<A> supplier,
                          BiConsumer<A, T> accumulator,
                          BinaryOperator<A> combiner,
                          Set<Characteristics> characteristics) {
                this(supplier, accumulator, combiner, castingIdentity(), characteristics);
            }
    
            @Override
            public BiConsumer<A, T> accumulator() {
                return accumulator;
            }
    
            @Override
            public Supplier<A> supplier() {
                return supplier;
            }
    
            @Override
            public BinaryOperator<A> combiner() {
                return combiner;
            }
    
            @Override
            public Function<A, R> finisher() {
                return finisher;
            }
    
            @Override
            public Set<Characteristics> characteristics() {
                return characteristics;
            }
        }
    
       private static <I, R> Function<I, R> castingIdentity() {
           return i -> (R) i;
       }
    }
    

    Sample usage:

    Map<Integer, List<Integer>> map =list.stream()
        .collect(Collectors.groupingBy(
             Collection::size,
             Nikollectors.flatMapping( // <<<
                 l -> l.stream().filter(i -> i % 2 == 0),
                 Collectors.toList()
            )
        )
    );
    
    0 讨论(0)
  • 2021-02-05 13:17

    For just this particular case, I guess this would be a simpler version:

    Map<Integer, List<Integer>> map =
            list.stream()
                .collect(Collectors.toMap(
                    Collection::size,
                    x -> x.stream().filter(y -> y % 2 == 0).collect(Collectors.toList())
                ));
    

    If there would be merging involved (two collections that would have the same size), I would add a merge function that is pretty trivial:

     Map<Integer, List<Integer>> map =
            list.stream()
                .collect(Collectors.toMap(
                    Collection::size,
                    x -> x.stream().filter(y -> y % 2 == 0).collect(Collectors.toCollection(ArrayList::new)),
                    (left, right) -> {
                        left.addAll(right);
                        return left;
                    }
                ));
    

    Otherwise, I agree with Michael in this comment, this is not that hard to back-port to java-8.

    0 讨论(0)
提交回复
热议问题