如何转换List <map <customer,map <key,=“”bigdecimal =“”>&gt;&gt;到地图<客户,地图<key,=“”bigdecimal =“”>&gt; </customer,> </map <customer,>

时间:2015-03-23 10:31:50

标签: java java-8 java-stream

我遇到了java8流问题 我有一个

List<Map<Customer, Map<key, BigDecimal>>> 

我想转换成

Map<Customer, Map<key, BigDecimal>> 

其中键是月份,BigDecimal是BigDecimals的总和。

我可以通过迭代来实现,但我想使用java8流,因为我认为它应该是可能的 当我迭代时,我使用临时的

Map<Customer, List<Map<key, BigDecimal>>>

我通过迭代减少每个客户。

但要用流做,我坚持将元素添加到临时列表中!

有人可以帮忙吗?

3 个答案:

答案 0 :(得分:2)

在java 9中,我们将获得flatMapping收集器(see here),这将使这种事情变得更简单。

您可以将flatMapping的代码向后移植到项目中,当java 9命中时,将静态导入替换为官方导入。它看起来像这样:

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 Collector.of(
            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().toArray(new Collector.Characteristics[0])
        );
    }

然后你会像这样使用它:

Map<Customer, Map<key, BigDecimal>> result = input.stream()
        .map(Map::entrySet)
        .flatMap(Set::stream)
        .collect(groupingBy(
            e->e.getKey(),
            flatMapping(
                e->e.getValue().entrySet().stream(), 
                toMap(e->e.getKey(), e->e.getValue(), BigDecimal::add)
            )
        ));

如果您不想这样做并且更愿意使用当前标准的收藏家,您可以使用3参数toMap,它结合了map和reduce:

Map<Customer, Map<key, BigDecimal>> result = input.stream()
        .map(Map::entrySet)
        .flatMap(Set::stream)
        .collect(toMap(
            e->e.getKey(),
            e->e.getValue(),
            (a,b) -> Stream.of(a,b)   // merge the 2 maps
                    .map(Map::entrySet)
                    .flatMap(Set::stream)
                    .collect(toMap(
                        e->e.getKey(),
                        e->e.getValue(), 
                        BigDecimal::add
                    ))
        ))

答案 1 :(得分:1)

API中缺少的是用于合并两个整个Map的函数(尽管存在合并单个映射的方法)。使用一个小辅助函数来创建这样的Collector

static <K,V> Collector<Map<K,V>,?,Map<K,V>> mergeMaps(BinaryOperator<V> op) {
    return Collector.of(()->new HashMap<>(),
        (m,n)->  n.forEach((k,v)->m.merge(k, v, op)),
        (m,n)->{ n.forEach((k,v)->m.merge(k, v, op)); return m; });
}

你可以解决你的任务,如

Map<Customer, Map<Key, BigDecimal>> collected =
    list.stream().flatMap(m->m.entrySet().stream())
        .collect(Collectors.groupingBy(Map.Entry::getKey,
            Collectors.mapping(Map.Entry::getValue, mergeMaps(BigDecimal::add))));

答案 2 :(得分:0)

我可以通过

进行排序
    private static final BinaryOperator<FactureMensuel> applatirListeFactureMensuelle = new BinaryOperator<FactureMensuel>() {
        @Override
        public FactureMensuel apply(FactureMensuel factureMensuel, FactureMensuel factureMensuel2) {
            factureMensuel2.forEach((k, v) -> factureMensuel.merge(k, v, BigDecimal::add));
            return factureMensuel;
        }
    };



oemInvoices.stream()
    .reduce((oemInvoice1, oemInvoice2) -> {
        oemInvoice2.forEach((k, v) -> oemInvoice1.merge(k, v, applatirListeFactureMensuelle));
        return oemInvoice1;
    }).get();

在地图上使用forEach来合并两个地图