Java8:HashMap<X, Y> 到 HashMap<X, Z> 使用 Stream / Map-Reduce / Collector

新手上路,请多包涵

我知道如何“转换”一个简单的 Java ListY -> Z ,即:

 List<String> x;
List<Integer> y = x.stream()
        .map(s -> Integer.parseInt(s))
        .collect(Collectors.toList());

现在我想对地图做基本相同的事情,即:

 INPUT:
{
  "key1" -> "41",    // "41" and "42"
  "key2" -> "42"      // are Strings
}

OUTPUT:
{
  "key1" -> 41,      // 41 and 42
  "key2" -> 42       // are Integers
}

解决方案不应限于 String -> Integer 。就像上面的 List 示例一样,我想调用任何方法(或构造函数)。

原文由 Benjamin M 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 746
2 个回答
Map<String, String> x;
Map<String, Integer> y =
    x.entrySet().stream()
        .collect(Collectors.toMap(
            e -> e.getKey(),
            e -> Integer.parseInt(e.getValue())
        ));

它不如列表代码好。您不能在 map() 调用中构造新的 Map.Entry s,因此工作混合到 collect() 调用中。

原文由 John Kugelman 发布,翻译遵循 CC BY-SA 3.0 许可协议

以下是 Sotirios Delimanolis 的答案 的一些变体,从 (+1) 开始非常好。考虑以下:

 static <X, Y, Z> Map<X, Z> transform(Map<? extends X, ? extends Y> input,
                                     Function<Y, Z> function) {
    return input.keySet().stream()
        .collect(Collectors.toMap(Function.identity(),
                                  key -> function.apply(input.get(key))));
}

这里有几点。首先是在泛型中使用通配符;这使得函数更加灵活。例如,如果您希望输出映射具有作为输入映射键的超类的键,则需要使用通配符:

 Map<String, String> input = new HashMap<String, String>();
input.put("string1", "42");
input.put("string2", "41");
Map<CharSequence, Integer> output = transform(input, Integer::parseInt);

(还有一个地图值的示例,但它确实是人为设计的,而且我承认使用 Y 的有界通配符仅在边缘情况下有帮助。)

第二点是,我没有在输入映射的 entrySet 上运行流,而是在 keySet 上运行它。我认为,这使代码更简洁,但代价是必须从地图中而不是从地图条目中获取值。顺便说一句,我最初将 key -> key 作为 toMap() 的第一个参数,但由于某种原因导致类型推断错误而失败。将其更改为 (X key) -> keyFunction.identity() 一样有效。

还有一种变化如下:

 static <X, Y, Z> Map<X, Z> transform1(Map<? extends X, ? extends Y> input,
                                      Function<Y, Z> function) {
    Map<X, Z> result = new HashMap<>();
    input.forEach((k, v) -> result.put(k, function.apply(v)));
    return result;
}

这使用 Map.forEach() 而不是流。我认为这更简单,因为它省去了收集器,这些收集器在与地图一起使用时有些笨拙。原因是 Map.forEach() 将键和值作为单独的参数给出,而流只有一个值——您必须选择是使用键还是映射条目作为该值。不利的一面是,这缺乏其他方法丰富、流畅的优点。 :-)

原文由 Stuart Marks 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题