使用Java Streams对内部Map进行分组

4

i have following structure:

Map<String,Map<String,Map<String,Integer>>>

现在我想忽略第一级映射并根据第二级映射的键分组(并汇总)第三级映射。

为了澄清一些示例条目:

Entry 1: ["1"["A"[[a,1];[b,2]];"B"[[a,3];[c,1]]]]
Entry 2: ["2"["A"[[b,2];[c,1]];"B"[[a,5];[b,0]]]]

期望的输出:

Entry 1: ["A"[[a,1];[b,4];[c,1]]]
Entry 4: ["B"[[a,8];[b,0];[c,1]]]

因此,为了做到这一点,我首先根据我的第二级键(“A”,“B”)对我的输入流进行分组,如果没有其他操作,最终得到以下结构:

Map<String,List<Entry<String,Map<String,Integer>>>>

我现在遇到的问题是如何从每个外部Map中获取我的条目列表Map<String,Integer>(特别是对于每个外部Map)。

以下是我认为肯定需要的简单代码:

        initialMap.values().stream()
                            .flatMap(m -> m.entrySet().stream())
                            .collect(Collectors.groupingBy(Map.Entry::getKey));

概述:

如何将Map<String,Map<String,Map<String,Integer>>>转换为Map<String<Map<String,Integer>>,忽略最外层的Map,根据第二层Keys对我的最内层Maps进行分组,并通过最内层Mapkey值求和Integer-values。 此外,每个外部的Map对于每个第二级别的Map都有一个Key-Value-Pair,因此每个都将具有相同的第二级别Keys。在第3层的Keyset中可能会有在其他第3层的Maps中找不到的Keys


总结一下,我建议使用.reduce - arnonuem
在流中很难保持外部迭代的上下文。那为什么不直接在没有它们的情况下进行迭代呢? - M. Prokhorov
你似乎使用地图只存储一个条目:Entry 1: ["1"["A"[a,1]]]。你是否考虑过这些地图是SingletonMap(只有一个条目的地图)?我问你这个问题,因为否则你问题中的输入输出太简单了。例如,对于此输入期望的结果是什么:["1"["A"[a,1], "B"[a,1]]] - davidxxx
@Proph3cy,你可以使用流来完成这个任务,但这并不是特别好的做法,也不是流的真正意图。 - M. Prokhorov
好的,我在我的例子中有点不准确...我会编辑它们。 - Proph3cy
显示剩余4条评论
3个回答

2
Map<String, Map<String, Integer>> result = 
    initialMap
        .values()
        .stream()
        .flatMap(m -> m.entrySet().stream())
        .collect(Collectors.groupingBy(Map.Entry::getKey, 
                                       Collectors.groupingBy(e -> mapToFirstEntry(e.getValue()).getKey(), 
                                                             Collectors.summingInt(e -> mapToFirstEntry(e.getValue()).getValue()))));

"最初的回答"假设一个三级的Map<String, Integer>包含一个条目,并且有一个获取该条目的方法:但是请注意,这只是一种假设。
public static Map.Entry<String, Integer> mapToFirstEntry(Map<String, Integer> map) {
    return map.entrySet().iterator().next();
} 

2
如果您有使用Java9的自由,我建议您使用flatMapping收集器来解决这个问题。这种方法更易读,对我来说生成的视觉杂乱更少。以下是它的样子。
Map<String, Map<String, Integer>> summaryMap = map.values().stream()
    .flatMap(m -> m.entrySet().stream())
    .collect(Collectors.groupingBy(Map.Entry::getKey,
        Collectors.flatMapping(e -> e.getValue().entrySet().stream(),
            Collectors.groupingBy(Map.Entry::getKey, 
                Collectors.summingInt(Map.Entry::getValue)))));

这个程序会产生以下输出:

{A={a=1, b=4, c=1}, B={a=8, b=0, c=1}}


1
这里需要记住的一件事是:从概念上讲,流代表通过某种“管道”传递下来的单个元素。当流运行时,它总是单个元素,无论源是否有一个、多个或无限数量的元素被累积在一起。
你在尝试表示几个嵌套循环,大致如下:
Map<String, Map<String, Integer>> result = new HashMap<>();
for (Map<String, Map<String, Integer>> firstMap : inputMap.values()) {
    for (Entry<String, Map<String, Integer>> firstEntry : firstMap.entrySet()) {
        String upperCaseKey = firstEntry.getKey();
        Map<String, Ingeter> resultEntry = result.computeIfAbsent(
            upperCaseKey,
            _k -> new HashMap<>());
        for (Entry<String, Integer> secondEntry : firstEntry.getValue().entrySet()) {

            resultEntry.merge(secondEntry.getKey(), secondEntry.getValue(), Integer::sum);

        }
    }
}

通过流进行更好的方法之一是通过Collector组合:

inputMap.values().stream()
    .flatMap(map -> map.entrySet().stream())

    .flatMap(firstEntry -> firstEntry.getValue()
                           .entrySet().stream()
                           .map(secondEntry -> new SimpleImmutableEntry(
                                                       firstEntry.getKey(),
                                                       secondEntry)
                           )
    )
    .collect(Collectors.groupingBy(
        Entry::getKey,
        Collectors.groupingBy(
          compositeEntry -> compositeEntry.getValue().getKey(),
          Collectors.summingInt(compositeEntry -> compositeEntry.getValue().getValue())
        )
    ));

一般来说,这应该可以解决问题,但请注意,我必须先建立一个组合条目,以保持元素计数为1,然后嵌套两个分组收集器。这就是为什么我认为像你这样的任务不适合使用API。很可能需要您给编译器一些帮助,因为它可能难以正确推断所有类型。此外,请注意,这不是唯一的方法:Stream API非常灵活,您很可能会看到许多其他完成相同任务的方式。

好的,谢谢分享整个内容^^会遵循您建议的基于迭代的解决方案(为了可读性),但是看到如何处理这样的问题仍然是一个巨大的帮助。 - Proph3cy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接