【问题标题】:How to merge List of Maps of Maps into a Map of Maps?如何将地图列表合并为地图地图?
【发布时间】:2019-05-02 22:47:33
【问题描述】:

你能帮我处理Java Streams吗?

从标题中可以看出,我需要将List<Map<String, Map<String, Genuineness>>> 合并到Map<String, Map<String, Genuineness>>

列表表示为List<Map<String, Map<String, Genuineness>>>,如下所示:

[  
   {  
      "USER_1":{  
         "APP_1":{  
            "total":1,
            "totalGenuine":1,
            "totalDevelopment":1
         }
      },
      "USER_2":{  
         "APP_1":{  
            "total":1,
            "totalGenuine":1,
            "totalDevelopment":1
         },
         "APP_2":{  
            "total":2,
            "totalGenuine":2,
            "totalDevelopment":2
         }
      }
   },
   {  
      "USER_1":{  
         "APP_1":{  
            "total":1,
            "totalGenuine":1,
            "totalDevelopment":1
         }
      },
      "USER_2":{  
         "APP_1":{  
            "total":1,
            "totalGenuine":1,
            "totalDevelopment":1
         },
         "APP_2":{  
            "total":2,
            "totalGenuine":2,
            "totalDevelopment":2
         }
      }
   }
]

因此,如您所见,重复键可能无处不在。 我的目标是通过合并Genuineness 将它们组合成Map<String, Map<String, Genuineness>>。合并Genuineness 只是意味着返回一个新对象,其中包含totaltotalGenuinetotalDevelopment 的总和值。

这是我失败的实现:

final Map<String, Map<String, Genuineness>> map = taskHandles.stream().map(this::mapTaskHandle)
                .flatMap(m -> m.entrySet().stream()).collect(
                        Collectors.toMap(Map.Entry::getKey, e -> e.getValue().entrySet().stream()
                                .collect(
                                        Collectors.toMap(Map.Entry::getKey,
                                                g -> new Genuineness(g.getValue().getTotal(), g.getValue().getTotalGenuine(), g.getValue().getTotalDevelopment()),
                                                (g1, g2) -> new Genuineness(g1.getTotal() + g2.getTotal(),
                                                        g1.getTotalGenuine() + g2.getTotalGenuine(),
                                                        g1.getTotalDevelopment() + g2.getTotalGenuine()
                                                )
                                        )
                                )
                        )
                );

失败并显示消息:

java.lang.IllegalStateException: Duplicate key {TEST_33_33_APP_1=live.attach.billing.domain.model.billing.Genuineness@951b6fe}

所以,似乎在我的实现中,我已经指出了如何合并内部地图,但没有合并外部地图的值,我不知道该怎么做。

非常感谢您的帮助。提前谢谢!

更新: 预期输出:

   {  
      "USER_1":{  
         "APP_1":{  
            "total":2,
            "totalGenuine":2,
            "totalDevelopment":2
         }
      },
      "USER_2":{  
         "APP_1":{  
            "total":2,
            "totalGenuine":2,
            "totalDevelopment":2
         },
         "APP_2":{  
            "total":4,
            "totalGenuine":4,
            "totalDevelopment":4
         }
      }
   }

【问题讨论】:

  • 请提供预期的输出,否则几乎不可能找出你在追求什么
  • 考虑使用 java 流的 flatMap 特性。它将为您将分层集合分解为扁平的对象流。
  • 嗨,谢谢,我已经添加了预期的输出和简化的输入数据
  • 我强烈建议将两个 Genuineness 对象的合并逻辑移动到 Genuineness 类中,例如添加一个Genuineness merge(Genuineness other) 方法,该方法返回一个新的合并Genuineness 对象。如果您需要向Genuineness 添加另一个需要合并的字段,它将简化您的代码并使其在以后变得更容易。基本上,让Genuineness 负责了解如何合并自身的两个(或更多)实例,即将逻辑放在它所属的位置。
  • 模式好不好?

标签: java hashmap java-stream collectors


【解决方案1】:

老实说,这是一个糟糕的数据结构,代码的维护者很难发现出现的问题。

你应该退后一步考虑重构代码,任何方法都可以通过在最外面的toMap中使用以下合并函数来解决缺失的部分:

(l, r) -> {
      r.forEach((k, v) -> l.merge(k, v,
                    (bi, bii) -> new Genuineness(bi.getTotal() + bii.getTotal(),
                               bi.getTotalGenuine() + bii.getTotalGenuine(),
                               bi.getTotalDevelopment() + bii.getTotalGenuine())));
       return l;
}

完整代码:

taskHandles.stream().map(this::mapTaskHandle)
                .flatMap(m -> m.entrySet().stream()).collect(
                        Collectors.toMap(Map.Entry::getKey, e -> e.getValue().entrySet().stream()
                                .collect(
                                        Collectors.toMap(Map.Entry::getKey,
                                                g -> new Genuineness(g.getValue().getTotal(), g.getValue().getTotalGenuine(), g.getValue().getTotalDevelopment()),
                                                (g1, g2) -> new Genuineness(g1.getTotal() + g2.getTotal(),
                                                        g1.getTotalGenuine() + g2.getTotalGenuine(),
                                                        g1.getTotalDevelopment() + g2.getTotalGenuine()
                                                )
                                        )
                                ),(l, r) -> {
                                  r.forEach((k, v) -> l.merge(k, v,
                                          (bi, bii) -> new Genuineness(bi.getTotal() + bii.getTotal(),
                                                  bi.getTotalGenuine() + bii.getTotalGenuine(),
                                                  bi.getTotalDevelopment() + bii.getTotalGenuine())));
                                  return l;
                                }

                        )
                );

Ideone

【讨论】:

  • (+1) 正在从先前的问题中寻找类似的解决方案,该问题被 OP 在其中一个线程中表示为地图缩减。
【解决方案2】:

虽然Aomine's suggested solution 似乎是正确的,但您也可以提高代码的可读性并简化它,将BinaryOperator&lt;Genuineness&gt; 定义为:

BinaryOperator<Genuineness> remappingGenuineness = (g1, g2) -> new Genuineness(g1.getTotal() + g2.getTotal(),
        g1.getTotalGenuine() + g2.getTotalGenuine(),
        g1.getTotalDevelopment() + g2.getTotalGenuine()
);

然后进一步将其用作:

final Map<String, Map<String, Genuineness>> map = taskHandles.stream()
        .flatMap(m -> m.entrySet().stream())
        .collect(Collectors.toMap(Map.Entry::getKey,
                e -> e.getValue().entrySet().stream().collect(
                        Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, remappingGenuineness)),
                (a, b) -> {
                    a.forEach((k, v) -> b.merge(k, v, remappingGenuineness));
                    return b;
                }));

【讨论】:

  • 是的,到目前为止看起来是一个不错的改进,我并没有考虑太多重构 OP 的代码,因为仅解决问题就消耗了我一些精力。 +1
猜你喜欢
  • 2017-12-23
  • 1970-01-01
  • 2022-01-01
  • 1970-01-01
  • 2017-12-25
  • 1970-01-01
  • 2015-06-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多