252

我知道如何ListY-> “转换”一个简单的Java 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上面的例子一样,我想调用任何方法(或构造函数)。

4

9 回答 9

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

它不如列表代码那么好。您不能Map.Entrymap()调用中构造 new ,因此工作会混入collect()调用中。

于 2014-09-18T02:21:17.793 回答
37

以下是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) -> key有效,就像Function.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()键和值作为单独的参数给出,而流只有一个值——您必须选择是使用键还是映射条目作为该值。不利的一面是,这缺乏其他方法的丰富、流畅的优点。:-)

于 2014-09-18T06:00:11.363 回答
17

A generic solution like so

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

Example

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));
于 2014-09-18T02:23:51.753 回答
16

Guava 的函数Maps.transformValues正是您要找的,它与 lambda 表达式配合得很好:

Maps.transformValues(originalMap, val -> ...)
于 2017-07-05T22:19:13.413 回答
11

它是否绝对必须是 100% 的功能和流畅的?如果不是,这个怎么样,它尽可能短:

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

如果你能忍受将流与副作用结合起来的羞耻和内疚

于 2016-01-06T21:38:59.203 回答
5

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

Map<String, Integer> output = EntryStream.of(input).mapValues(Integer::valueOf).toMap();
于 2016-01-29T04:52:04.877 回答
4

出于学习目的而始终存在的另一种方法是通过 Collector.of() 构建您的自定义收集器,尽管此处的 toMap() JDK 收集器很简洁(此处为 +1

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;}
               ));
于 2016-06-26T02:39:18.847 回答
3

如果您不介意使用 3rd 方库,我的cyclops-react库具有所有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);
于 2016-02-23T11:58:59.737 回答
-1

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

map.replaceAll((key, val) -> getUpdatedListFor(key, val));

yourMutableMap.replaceAll((key, val) return_value_of_bi_your_function); NB。请注意您正在修改地图状态。所以这可能不是你想要的。

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

于 2018-02-10T13:02:00.453 回答