Java 8 Streams:Make Collectors.groupingBy返回Map <k,list <v =“”>&gt;而不是Map <k,列出<list <v =“”>&gt;&gt;

时间:2017-05-19 21:34:27

标签: java java-8 java-stream

我有List<SomeBean>SomeBean包含一个返回K的方法,该方法可能在列表中重复,另一个方法返回List<V>。由于我使用SomeBean的方式,它几乎类似于具有键KList<V>的条目。

我想将其转换为Map<K, List<V>>,其中List<V>是具有相同键K的列表的串联。 这就是我所做的:

private static Map<K, List<V>> transformToMapOfListInTwoSteps(List<SomeBean> paginationResult) {
    Map<K, List<List<V>>> mapOfListOfList = paginationResult.stream()
            .collect(Collectors.groupingBy(SomeBean::getK, Collectors.mapping(SomeBean::getV, Collectors.toList())));

    return mapOfListOfList.entrySet().stream()
            .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().stream().flatMap(Collection::stream).collect(Collectors.toList())));
}

然而,这目前分两步完成。如何在不使用中间一次性Map<K, List<List<V>>>的情况下执行相同的操作?

2 个答案:

答案 0 :(得分:5)

我认为你过于复杂化了一些事情。我只会使用Collectors.toMap的重载接受合并函数,以便在键上发现碰撞时合并这些值:

private static <K, V> Map<K, List<V>> transformToMapOfListInOneStep(
        List<SomeBean> paginationResult) {

    return paginationResult.stream()
        .collect(Collectors.toMap(
            SomeBean::getK, 
            someBean -> new ArrayList<>(someBean.getV()), 
            (v1, v2) -> { v1.addAll(v2); return v1; }));
}

注意:我在值映射器函数中创建了一个新的ArrayList因为我不想修改属于SomeBean的原始列表。

另一种方法是使用Collectors.groupingBy以及对值进行平面映射的下游收集器,但这不是在Java 8中实现的(尽管它将在Java 9中提供,请参阅Collectors.flatMapping )。

在Java 8中,这可以实现为:

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> acc = downstream.accumulator();
    return Collector.of(downstream.supplier(),
        (a, t) -> {
            try (Stream<? extends U> s = mapper.apply(t)) {
                if (s != null) s.forEachOrdered(u -> acc.accept(a, u));
            }
        },
        downstream.combiner(), downstream.finisher(),
        downstream.characteristics().toArray(new Collector.Characteristics[0]));
}

你可以这样使用它:

return paginationResult.stream()
        .collect(Collectors.groupingBy(
                SomeBean::getK,
                flatMapping(
                    someBean -> someBean.getV().stream(),
                    Collectors.toList())));

注2:我从this answer given by user @Holger开始实施flatMapping方法。

答案 1 :(得分:1)

使用其他下游收集器collectingAndThen

private static Map<K, List<V>> transformToMapOfList(List<SomeBean> paginationResult) {
    return paginationResult.stream()
            .collect(Collectors.groupingBy(
                    SomeBean::getK,
                    Collectors.mapping(
                            SomeBean::getV,
                            Collectors.collectingAndThen(
                                    Collectors.toList(),
                                    lists -> lists.stream().flatMap(Collection::stream).collect(Collectors.toList())
                            )
                    )
            ));
}

collectingAndThenCollectors.toList()相结合,可以在结果集合上应用函数,允许将其映射到任何类型。

由于没有在网上找到任何答案,我试图解决这个问题,在试图做一些疯狂的低效率还原操作时,我的头脑几个小时,这让我的IDE和编译器严重消化不良。

enter image description here