【问题标题】:Group List of Map Data into Nested HashMap in Java将地图数据列表分组到 Java 中的嵌套 HashMap
【发布时间】:2019-03-21 06:49:42
【问题描述】:

输入:我有一个 Map 列表(键 - 值对)和分组键

例如List<Map<String,String>> inputDatajson

[
 {
    'a': 'German',
    'b': 'Audi',
    'e': 'T3'
 },
 {
    'a': 'German',
    'b': 'BMW',
    'c': 'T6'
 },
 {
    'a': 'American',
    'b': 'Tesla',
    'c': 'T6'
 }
]

和 groupingKeys= a and b

对于分组键 a -> 我将创建两个存储桶,一个用于德国和其他美国人,在每个存储桶中我将再次为 b 的值创建存储桶,检查以下内容

我想生成以下内容:Map<String, Map<String, List<Map>> - maps 的嵌套 == 分组键的数量,这里是 2

{
   German: {
       Audi: [
            {
               'a': 'German',
               'b': 'Audi',
               'e': 'T3'
            }
       ],
       BMW: [
            {
               'a': 'German',
               'b': 'BMW',
               'c': 'T6'
            }
       ]
   },
   American: {
      Tesla: [
          {
            'a': 'American',
            'b': 'Tesla',
            'c': 'T6'
          }
      ]
   }
}

我尝试过的事情:

inputData.stream().collect(Collectors.groupingBy(mapItem -> this.constructGroupingKey(groupingKeys, mapItem)));

//constructGroupingKey - this joins the values of of given grouping keys

这样我可以构造连接键,例如 German-Audi OR German-BMW 并保存匹配的项目,但这不是我想要的。

正如@Naman 在这里所说的具体类 - 以下使它成为一个衬里,但我不能对数据做出假设,它是 Map 类型的

Map<String, Map<String, List<ConcreteClass>>> output = inputData.stream().collect(Collectors.groupingBy(ConcreteClass::getCountry, Collectors.groupingBy(ConcreteClass::getCarType)));

希望有其他想法,谢谢。我也很好奇是否可以在没有指定分组键的情况下根据给定数据派生分组。

输入是动态的,键和值由输入定义

【问题讨论】:

  • 使用具体的对象!另一种方法是尝试分享您的输出类型,并查看其中驱动的复杂性。
  • 您可以使用嵌套地图吗? Map>> 其中第一个 Map 的键是国家/地区,第二个 Map 的键是公司,最后一个存储模型?
  • 可以,我可以根据键的性质自动派生吗,嵌套会根据分组键的数量增加,我不能假设它只是两级嵌套
  • @Naman,输入是 Map 类型,我不能对数据做出假设,创建具体类需要我指定字段名称?

标签: java hashmap java-stream grouping collectors


【解决方案1】:

只需双击作为具体类的输入表示。它看起来会更简单和干净:

Map<String, Map<String, List<ThrowAway>>> multipleGrouping(List<ThrowAway> inputData) {
    return inputData.stream()
            .collect(Collectors.groupingBy(ThrowAway::getA,
                    Collectors.groupingBy(ThrowAway::getB)));
}

我刚刚创建了一个与您的地图表示相同的示例类:

class ThrowAway {
    String a;
    String b;
    String c;

    String getA() {
        return a;
    }

    String getB() {
        return b;
    }
}

【讨论】:

  • 糟糕的命名约定!同意,但更多的是在这里描述方法。
  • 你是说从 Map 的给定输入——>派生类 ThrowAway 吗?输入会改变,我们不能把所有东西都映射到 ThrowAway 吗?
  • @Pradeep 我想传达的是,根据问题本身假设了某些保证。例如是否可以以[ { 'a': 'German', 'b': 'Audi', 'e': 'T3' }, { 'a': 'German', 'b': 'BMW', 'c': 'T6' }, { 'd': 'American', 'e': 'Tesla', 'f': 'T6' } ] 的形式输入,然后您期望输出是什么?必须有某种契约来确定至少要分组的密钥。
  • 我们不能假设输入形式,但我可以要求调用者预先注册分组键,输出总是基于分组键嵌套
  • @Pradeep 和预注册密钥在我的理解中定义了一个具体的对象。此外,您似乎忽略了继承在这里可以发挥的作用。
【解决方案2】:

由于您正在寻找动态解决方案,这里有另一种方法:

您可以使用以下分组方法来传递两个键:

public Map<String, Map<String, List<Map<String, String>>>> group(List<Map<String, String>> list, String key1, String key2) {
    return list.stream().collect(
            Collectors.groupingBy(m -> m.get(key1),
                    Collectors.groupingBy(m -> m.get(key2))
            )
    );
}

这里的问题是,如果地图中不存在密钥,您将获得NullPointerException。为防止这种情况,您可以使用 map.getOrDefault() 和默认密钥:

public Map<String, Map<String, List<Map<String, String>>>> group(List<Map<String, String>> list, String key1, String key2, String noneKey) {
    return list.stream().collect(
            Collectors.groupingBy(m -> m.getOrDefault(key1, noneKey),
                    Collectors.groupingBy(m -> m.getOrDefault(key2, noneKey))
            )
    );
}

使用以下选项调用方法之一:

Map<String, Map<String, List<Map<String, String>>>> result = group(list, "a", "b");

或者

Map<String, Map<String, List<Map<String, String>>>> result = group(list, "a", "b", "none");

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-27
    • 2020-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多