Java 8 流将 List<Map<>> 通过相同的 <Key, Value> 分组到新的 List<Map<>>

Bet*_*Man 5 java java-8 java-stream collectors groupingby

我有一个List<Map<String,String>> 这样的:

Map<String, String> m1 = new HashMap<>();
m1.put("date", "2020.1.5");
m1.put("B", "10");

Map<String, String> m2 = new HashMap<>();
m2.put("date", "2020.1.5");
m2.put("A", "20");

Map<String, String> m3 = new HashMap<>();
m3.put("date", "2020.1.6");
m3.put("A", "30");

Map<String, String> m4 = new HashMap<>();
m4.put("date", "2020.1.7");
m4.put("C", "30");

List<Map<String, String>> before = new ArrayList<>();
before.add(m1);
before.add(m2);
before.add(m3);
before.add(m4);
Run Code Online (Sandbox Code Playgroud)

我期望的结果是生成一个新的 List 映射,该映射按 date 分组,并将同一日期中设置的所有条目放在一起,例如:

[{"A":"20","B":"10","date":"2020.1.5"},{"A":"30","date":"2020.1.6"},{"C":"30","date":"2020.1.7"}]
Run Code Online (Sandbox Code Playgroud)

我尝试了以下方法,但始终不是我期望的结果。

stream().flatmap().collect(Collectors.groupingBy())
Run Code Online (Sandbox Code Playgroud)

对这个问题的一些附加评论:

我为 LOOP 解决了这个问题,但是当列表大小约为 50000 时应用程序挂起,所以我寻求一种更好的性能方法来做到这一点。据我所知,Java 8 流平面地图可能是一种方式。所以关键不仅是重新映射这个,而且是用最高效的方式来做到这一点。

Sil*_*cea 7

before
  .stream()
  .collect(Collectors.toMap((m) -> m.get("date"), m -> m, (a,b) -> {
      Map<String, String> res = new HashMap<>();
      res.putAll(a);
      res.putAll(b);
      return res;
  }))
  .values();
Run Code Online (Sandbox Code Playgroud)

这是您正在寻找的解决方案。

toMap函数接收 3 个参数:

  • 键映射器,在您的情况下是日期
  • 值映射器,即正在处理的映射本身
  • 合并函数,它需要 2 个具有相同日期的地图并将所有的键放在一起

输出:

[{date=2020.1.5, A=20, B=10}, {date=2020.1.6, A=30}, {date=2020.1.7, C=30}]
Run Code Online (Sandbox Code Playgroud)


Ekl*_*vya 5

您可以使用groupingBy和这样做Collector.of

List<Map<String, String>> list = new ArrayList<>(before.stream()
        .collect(Collectors.groupingBy(
                k -> k.get("date"),
                Collector.of( HashMap<String,String>::new,
                        (m,e)-> m.putAll(e),
                        (map1,map2)->{ map1.putAll(map2); return map1;}
                ))).values());
Run Code Online (Sandbox Code Playgroud)

在这里,首先使用Collectors.groupingBy按日期分组。然后定义自定义收集器Collector.of用于收集List<Map<String, String>>Map<String, String>. 使用地图值创建列表后。

Collectors.flatMappingJava 9 开始使用

List<Map<String, String>> list = new ArrayList<>(before.stream()
        .collect(Collectors.groupingBy(
                k -> k.get("date"),
                Collectors.flatMapping(m -> m.entrySet().stream(), 
                    Collectors.toMap(k -> k.getKey(), v -> v.getValue(), (a,b) -> a))))
               .values());
Run Code Online (Sandbox Code Playgroud)