【问题标题】:Convert type X to Y in Map<K, Map<V, X>> using Java Stream API使用 Java Stream API 将 Map<K, Map<V, X>> 中的 X 类型转换为 Y
【发布时间】:2017-11-01 04:03:39
【问题描述】:

我想从地图的地图转换内部地图。

旧地图:Map&lt;String, Map&lt;LocalDate, Integer&gt;&gt;整数表示秒

新地图:Map&lt;String, Map&lt;LocalDate, Duration&gt;&gt;

我已尝试创建新的内部地图,但出现错误

错误:java:没有找到适合putAll(java.util.stream.Stream&lt;java.lang.Object&gt;) 的方法 方法java.util.Map.putAll(java.util.Map&lt;? extends java.time.LocalDate,? extends java.time.Duration&gt;)不适用

oldMap.entrySet().stream()
            .collect(Collectors.toMap(Map.Entry::getKey,
                e -> new HashMap<LocalDate, Duration>() {{
                    putAll(
                        e.getValue().entrySet().stream()
                            .map(x -> new HashMap.SimpleEntry<LocalDate, Duration>
                                (x.getKey(), Duration.ofSeconds(x.getValue())))
                    );
                }}
            ));

【问题讨论】:

标签: java dictionary java-8 java-stream collectors


【解决方案1】:

我更愿意遍历旧地图的条目并在内部地图上流式传输:

for (Entry<String, Map<LocalDate, Integer>> entry : oldMap.entrySet()) {
    Map<LocalDate, Duration> asDuration = entry.getValue().entrySet().stream()
        .collect(Collectors.toMap(e -> e.getKey(), e -> Duration.ofSeconds(e.getValue().longValue())));
    newMap.put(entry.getKey(), asDuration);
}

否则,您需要在 collect 中添加第二个流:

newMap = oldMap.entrySet().stream()
        .collect(Collectors.toMap(s -> s.getKey(), s -> s.getValue().entrySet().stream()
        .collect(Collectors.toMap(e -> e.getKey(), e -> Duration.ofSeconds(e.getValue().longValue())))));

【讨论】:

  • first answer第二个变种的主要思想
  • @Alex78191 是的,他有点快
【解决方案2】:

如果我理解正确,您可以看到以下代码:

oldMap.entrySet().stream()
            .collect(Collectors.toMap(x -> x.getKey(),
                    x -> {
                        Map<LocalDate, Duration> temp = new HashMap<>();
                        x.getValue().forEach((k, v) -> {
                            temp.put(k, Duration.ofSeconds(v));
                        });
                        return temp;
                    })

            );

【讨论】:

    【解决方案3】:

    如果你想要紧凑的代码,你可以使用

    Map<String, Map<LocalDate, Duration>> newMap = new HashMap<>();
    oldMap.forEach((s,o) -> o.forEach((d, i) ->
        newMap.computeIfAbsent(s, x->new HashMap<>()).put(d, Duration.ofSeconds(i))));
    

    如果你想避免不必要的哈希操作,你可以稍微扩展一下

    Map<String, Map<LocalDate, Duration>> newMap = new HashMap<>();
    oldMap.forEach((s,o) -> {
        Map<LocalDate, Duration> n = new HashMap<>();
        newMap.put(s, n);
        o.forEach((d, i) -> n.put(d, Duration.ofSeconds(i)));
    });
    

    【讨论】:

    • 你用链子连接它的方式......花了一段时间才得到它。不错
    【解决方案4】:

    快速干净

    HashMap<String, HashMap<LocalDate, Duration>> newMap = new HashMap<>();
    oldHashMap.forEach((key, innerMap) -> {
        HashMap<LocalDate, Duration> newStuff = new HashMap<>();
        innerMap.forEach((k2,v2) -> newStuff.put(k2,Duration.ofSeconds(v2)));
        newMap.put(key, newStuff);
    });
    

    【讨论】:

    • @Holger 你速度更快,按最旧排序答案。
    • 9 秒,人生就是一场竞赛 :)
    【解决方案5】:

    还有一个……

     Map<String, Map<LocalDate, Duration>> newMap = map.entrySet().stream().collect(
                Collectors.toMap(Entry::getKey,
                        entry -> entry.getValue().entrySet().stream().collect(
                                Collectors.toMap(Entry::getKey, e -> Duration.ofSeconds(e.getValue())))));
    

    【讨论】:

    • @Alex78191 确实,非常接近!
    • 主要思想来自first answer
    • @Alex78191 可能是,我没看过那些。
    【解决方案6】:

    让我们提取一个辅助方法toMap 以使问题更简单。

    Map<String, Map<LocalDate, Duration>> result = oldMap.entrySet().stream()
        .map(entry -> new AbstractMap.SimpleEntry<>(
             entry.getKey(),
             entry.getValue().entrySet().stream()
                             .collect(toMap(it -> Duration.ofSeconds(it.longValue())))
    //                                ^--- mapping Integer to Duration
        )).collect(toMap(Function.identity()));
    
    
    
    <K, V, R> Collector<Map.Entry<K, V>, ?, Map<K, R>> toMap(Function<V, R> mapping) {
        return Collectors.toMap(
             Map.Entry::getKey,
             mapping.compose(Map.Entry::getValue)
        );
    }
    

    AND您可以进一步简化代码,因为主要逻辑是重复的:将一个值调整为 Map 中的另一个值。

    Function<
        Map<String, Map<LocalDate, Integer>>,
        Map<String, Map<LocalDate, Duration>>
    > mapping = mapping(mapping(seconds -> Duration.ofSeconds(seconds.longValue())));
    //          |       |
    //          |   adapts Map<LocalDate, Integer> to Map<LocalDate,Duration>
    //          | 
    //adapts Map<String,Map<LocalDate,Integer>> to Map<String,Map<LocalDate,Duration>>
    
    Map<String, Map<LocalDate, Duration>> result = mapping.apply(oldMap);
    
    
    <K, V1, V2> Function<Map<K, V1>, Map<K, V2>> mapping(Function<V1, V2> mapping) {
        return it -> it.entrySet().stream().collect(toMap(mapping));
    }
    

    那么您可以在任何需要Function&lt;Map&lt;?,?&gt;,Map&lt;?,?&gt;&gt; 将值调整为另一个值的地方使用它,例如:

    Map<String, Map<LocalDate, Duration>> result = Optional.of(oldMap)
            .map(mapping(mapping(seconds -> Duration.ofSeconds(seconds.longValue()))))
            .get();
    

    【讨论】:

      【解决方案7】:

      我的两分钱,创建一个将Map&lt;K, V1&gt;转换为Map&lt;K, V2&gt;的方法:

      public static <K,V1,V2> Map<K, V2> transformValues(final Map<K, V1> input, final Function<V1,V2> transform) {
          Function<Map.Entry<K, V1>, V2> mapper = transform.compose(Map.Entry::getValue);
          return input.entrySet().stream()
                  .collect(toMap(Map.Entry::getKey, mapper));
      }
      

      那么你的代码就变成了:

      Map<String, Map<LocalDate, Duration>> transformed 
          = transformValues(maps, map -> transformValues(map, Duration::ofSeconds));
      

      【讨论】:

        【解决方案8】:

        为了完整起见,这里有一个使用 Guava 的 Maps.transformValues 的版本:

        Map<String, Map<LocalDate, Duration>> result = 
            Maps.transformValues(oldMap, m -> Maps.transformValues(m, Duration::ofSeconds));
        

        【讨论】:

        • 我忘记了。谢谢提醒。我已经重构了看起来像你的答案,但有人已经同时回答了我的想法。所以我添加了map(Function&lt;Map,Map&gt;) 的另一种用法。
        • @holi-java 嗨!我赞成你的回答。我喜欢它应用一个函数来懒惰地转换值。
        • 谢谢,先生。但我想说,在这种情况下,你的最后一种方法更好。
        • @holi-java 哦,非常感谢!番石榴是如此的实用和优雅。尽管它与您的答案不完全相同,因为番石榴返回地图视图。这些函数是惰性应用的,即在访问地图视图的方法时。
        • 是的,先生。实际映射发生在每个 get(?) 在运行时,因此它不会将 Map 复制到另一个 Map 只是在包装类中引用 Map
        【解决方案9】:

        这是StreamEx的解决方案:

        EntryStream.of(oldMap)
                   .mapValues(v -> EntryStream.of(v).mapValues(Duration::ofSeconds).toMap())
                   .toMap();
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-05-03
          • 2019-01-23
          • 1970-01-01
          • 1970-01-01
          • 2014-05-09
          • 2018-11-23
          • 2011-12-02
          • 1970-01-01
          相关资源
          最近更新 更多