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