我知道如何改变"来自List
的简单Java Y
- > 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
示例一样,我想调用任何方法(或构造函数)。
答案 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) -> 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()
将键和值作为单独的参数给出,而流只有一个值 - 您必须选择是使用键还是映射条目作为该值。从缺点来看,这缺乏其他方法的丰富,优雅的优点。 : - )
答案 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); 铌。请注意修改地图状态。所以这可能不是你想要的。