Java8:HashMap <x,y =“”>到HashMap <x,z =“”>使用Stream / Map-Reduce / Collector </x,> </x,>

时间:2014-09-18 02:14:03

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

我知道如何改变&#34;来自List的简单Java Y - &gt; Z,即:

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

现在我想用Map做基本相同的事,即:

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

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

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

9 个答案:

答案 0 :(得分:297)

Map<String, String> x;
Map<String, Integer> y =
    x.entrySet().stream()
        .collect(Collectors.toMap(
            e -> e.getKey(),
            e -> Integer.parseInt(e.getValue())
        ));

它不如列表代码那么好。您无法在Map.Entry来电中构建新的map(),因此工作会混合到collect()来电中。

答案 1 :(得分:32)

以下是Sotirios Delimanolis' answer的一些变体,这一点非常好(+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()将键和值作为单独的参数给出,而流只有一个值 - 您必须选择是使用键还是映射条目作为该值。从缺点来看,这缺乏其他方法的丰富,优雅的优点。 : - )

答案 2 :(得分:11)

像这样的通用解决方案

public static <X, Y, Z> Map<X, Z> transform(Map<X, Y> input,
        Function<Y, Z> function) {
    return input
            .entrySet()
            .stream()
            .collect(
                    Collectors.toMap((entry) -> entry.getKey(),
                            (entry) -> function.apply(entry.getValue())));
}

实施例

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

答案 3 :(得分:9)

它绝对必须100%功能和流畅吗?如果不是这样的话,那就差不多了:

Map<String, Integer> output = new HashMap<>();
input.forEach((k, v) -> output.put(k, Integer.valueOf(v));

if you can live with the shame and guilt of combining streams with side-effects

答案 4 :(得分:7)

Guava的函数Maps.transformValues正是您正在寻找的,它与lambda表达式很好地协同工作:

Maps.transformValues(originalMap, val -> ...)

答案 5 :(得分:5)

我的StreamEx库增强了标准流API,提供了EntryStream类,更适合转换地图:

Map<String, Integer> output = EntryStream.of(input).mapValues(Integer::valueOf).toMap();

答案 6 :(得分:3)

如果您不介意使用第三方库,我的cyclops-react lib包含所有JDK Collection类型的扩展程序,包括Map。我们可以直接使用'map'运算符转换地图(默认情况下,地图会对地图中的值进行处理)。

   MapX<String,Integer> y = MapX.fromMap(HashMaps.of("hello","1"))
                                .map(Integer::parseInt);

bimap可用于同时转换键和值

  MapX<String,Integer> y = MapX.fromMap(HashMaps.of("hello","1"))
                               .bimap(this::newKey,Integer::parseInt);

答案 7 :(得分:3)

学习目的始终存在的替代方法是通过Collector.of()构建自定义收集器,尽管toMap()JDK收集器在这里是简洁的(+ 1 here)。

Map<String,Integer> newMap = givenMap.
                entrySet().
                stream().collect(Collector.of
               ( ()-> new HashMap<String,Integer>(),
                       (mutableMap,entryItem)-> mutableMap.put(entryItem.getKey(),Integer.parseInt(entryItem.getValue())),
                       (map1,map2)->{ map1.putAll(map2); return map1;}
               ));

答案 8 :(得分:0)

声明性和简单的解决方案是:

yourMutableMap.replaceAll((key,val) - &gt; return_value_of_bi_your_function); 铌。请注意修改地图状态。所以这可能不是你想要的。

欢呼: http://www.deadcoderising.com/2017-02-14-java-8-declarative-ways-of-modifying-a-map-using-compute-merge-and-replace/