5

我有一个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);

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

[{"A":"20","B":"10","date":"2020.1.5"},{"A":"30","date":"2020.1.6"},{"C":"30","date":"2020.1.7"}]

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

stream().flatmap().collect(Collectors.groupingBy())

此问题的一些附加评论:

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

4

4 回答 4

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();

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

toMap函数接收 3 个参数:

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

输出:

[{date=2020.1.5, A=20, B=10}, {date=2020.1.6, A=30}, {date=2020.1.7, C=30}]
于 2020-06-15T14:23:26.720 回答
5

你可以这样做使用groupingByCollector.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());

在这里,首先使用Collectors.groupingBy按日期分组。Collector.of然后使用to collect List<Map<String, String>>into定义自定义收集器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());
于 2020-06-15T15:44:45.727 回答
1

您可以使用一定数量的 Collector 来实现相同的结果,顺序如下:

  • Collectors.groupingBy按日期分组
  • Collectors.reducing合并Map<String, String>项目
  • Collectors.collectingAndThen将值从 转换Map<String, Optional<Map<String, String>>>为先前归约到最终输出的结果List<Map<String, String>>
List<Map<String, String>> list = before.stream()
    .collect(Collectors.collectingAndThen(
        Collectors.groupingBy(
            m -> m.get("date"),
            Collectors.reducing((l, r) -> {
                l.putAll(r);
                return l; })
        ),
        o -> o.values().stream()
                       .flatMap(Optional::stream)
                       .collect(Collectors.toList())));

list包含您要查找的内容:

[{date=2020.1.5, A=20, B=10}, {date=2020.1.6, A=30}, {date=2020.1.7, C=30}]

重要提示:此解决方案有两个缺点:

  • 它看起来很笨拙,对于独立的观众来说可能不清楚
  • 它改变(修改)包含在List<Map<String, String>> before.
于 2020-06-15T14:34:47.977 回答
0

可以按如下方式完成:

List<Map<String, String>> remapped = before.stream()
    .collect(Collectors.groupingBy(m -> m.get("date")))
    .values().stream()
    .map(e -> e.stream()
               .flatMap(m -> m.entrySet().stream())
               .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (x1, x2) -> x1)))
    .collect(Collectors.toList());

remapped.forEach(System.out::println);

输出:

{date=2020.1.5, A=20, B=10}
{date=2020.1.6, A=30}
{date=2020.1.7, C=30}
于 2020-06-15T14:36:45.020 回答