以下是对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))));
}
这里有几个要点。首先是在泛型中使用通配符,这使得函数更加灵活。如果你想让输出的映射表的键是输入映射表键的超类,那么就需要使用通配符:
A couple points here. First is the use of wildcards in the generics; this makes the function somewhat more flexible. A wildcard would be necessary if, for example, you wanted the output map to have a key that's a superclass of the input map's 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()
而不是流(streams)。我认为这甚至更简单,因为它省去了与 Map 一起使用有些笨拙的收集器(collectors)的步骤。原因是 Map.forEach()
输出键和值作为分开的参数,而流只有一个值--你必须选择使用键还是地图条目作为该值。缺点是,这种方法缺乏其他方法中丰富的流优美性质。:-)
Map.Entry::getKey
来代替e -> e.getKey()
,但这只是个人口味和编程风格的问题。 - Holger