【发布时间】:2014-12-19 18:31:24
【问题描述】:
给定一些地图,是否有一种单行方式将它们的所有条目放入一张地图中?
忽略空值、覆盖条目等问题,我想编码的是:
public static <K, V> Map<K, V> reduce(Map<K, V>... maps) {
return Arrays.stream(maps)
.reduce(new HashMap<K, V>(), (a, b) -> a.putAll(b));
}
但这会产生编译错误,因为a.putAll(b) 是void。如果它返回this,它将起作用。
为了解决这个问题,我编写了代码:
public static <K, V> Map<K, V> reduce(Map<K, V>... maps) {
return Arrays.stream(maps)
.reduce(new HashMap<K, V>(), (a, b) -> {a.putAll(b); return a;});
}
它可以编译和工作,但它是一个丑陋的 lambda;编码return a; 感觉是多余的。
一种方法是重构一个实用方法:
public static <K, V> Map<K, V> reduce(Map<K, V> a, Map<K, V> b) {
a.putAll(b);
return a;
}
清理 lambda:
public static <K, V> Map<K, V> reduce(Map<K, V>... maps) {
return Arrays.stream(maps)
.reduce(new HashMap<K, V>(), (a, b) -> reduce(a, b));
}
但现在我有一个可重复使用但有点无用的实用方法。
有没有更优雅的方式来调用累加器上的方法并在 lambda 中返回它?
【问题讨论】:
-
不幸的是,
reduce的这种使用方式从根本上被破坏了:它可能适用于顺序执行,但它会在并行执行时中断。这是因为 reduce() 方法将其第一个(身份)参数视为可重用值。因此它适用于原始类型和不可变类型,但并行流上的可变值(如new HashMap<>())将由不同的线程同时发生变异。那不会有好的结局。答案是使用collect(),就像@Pshemo 的答案一样。