我知道如何List
从Y
- > "转换"一个简单的Java Z
,即:
Listx; List 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
- > Integer
.就像List
上面的例子一样,我想调用任何方法(或构造函数).
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()
呼叫中构建新的s,因此工作会混合到collect()
呼叫中.
Guava的功能Maps.transformValues
是你正在寻找的,它与lambda表达式很好地配合:
Maps.transformValues(originalMap, val -> ...)
以下是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()
将键和值作为单独的参数,而流只有一个值 - 您必须选择是使用键还是映射条目作为该值.从缺点来看,这缺乏其他方法的丰富,流动的优点.:-)
我的StreamEx库增强了标准流API,提供了一个EntryStream
更适合转换地图的类:
Map<String, Integer> output = EntryStream.of(input).mapValues(Integer::valueOf).toMap();
像这样的通用解决方案
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));
它绝对必须100%功能和流畅吗?如果没有,那么这个怎么样,就像它得到的一样短:
Map<String, Integer> output = new HashMap<>(); input.forEach((k, v) -> output.put(k, Integer.valueOf(v));
(如果你能忍受将流与副作用结合起来的羞耻感和内疚感)