嵌套分组依据为地图的收集器java 8

问题描述

我正在尝试处理具有以下格式的列表: List<Map<String,Map<String,Item>>Map<String,List<Item>>,其中Item是包含两个属性的对象。

因此输入:

[
 <"A1",<"B1",Item_1>>,<"A1",Item_2>>,<"B2",Item_3>>,<"A2",Item_4>>,Item_5>>,Item_6>>
]

输出应为:

"A1" {
    <"B1",[Item_1,Item_2]>
    <"B2",[Item_3]>
}
"A2" {
    <"B1",[Item_4]>
    <"B2",[Item_5,Item_6]>
}

我尝试使用:

list.stream()
.flatMap(it -> it.entrySet().stream())
.collect(groupingBy(Map.Entry::getKey,Collectors.mapping(Map.Entry::getValues,Collectors.toList())));

但结果不是期望的结果,它返回Map<String,List<Map<String,Item>>>

{
"A1": [
   <"B1",Item_1>,Item_2>,Item_3>
  ]
"A2": [
   <"B1",Item_4>,Item_5>,Item_6>
  ]
}

您能建议我如何同时按B_键进行分组吗?谢谢!

解决方法

如果可以将条目展平为三元组数据结构,则可以使用B_键进行分组。尽管这对于映射条目是可行的,但在实现方面却相当混乱。更好的选择是创建自己的数据结构以临时保存这三个扁平化的属性。

这是我将在解决方案中进一步使用的类似代码之一。

@Getter
@AllArgsConstructor
static class Holder {
    String a;
    String b;
    Item c;
}

现在,另一个重要的点是确保平整结构,直到代码中共享的最里面的条目而不只是最外面的Map为止。这将需要使用嵌套的flatMap

list.stream()
        .flatMap(out -> out.entrySet().stream()
                .flatMap(e -> e.getValue().entrySet().stream()
                        .map(in -> new Holder(e.getKey(),in.getKey(),in.getValue()))))

到最后,您将拥有一个Stream<Holder>,可以将其进一步分组两次并映射以推断预期的结果。这是针对此问题的完整解决方案:

list.stream()
        .flatMap(out -> out.entrySet().stream()
                .flatMap(e -> e.getValue().entrySet().stream()
                        .map(in -> new Holder(e.getKey(),in.getValue()))))
        .collect(Collectors.groupingBy(Holder::getA,Collectors.groupingBy(Holder::getB,Collectors.mapping(Holder::getC,Collectors.toList()))));