首先,一些上下文代码:
import java.util.*;
import java.util.concurrent.atomic.DoubleAdder;
import java.util.function.Function;
import java.util.stream.Collectors;
class Scratch {
static enum Id {A, B, C}
static class IdWrapper {
private final Id id;
public IdWrapper(Id id) {this.id = id;}
Id getId() { return id; }
}
public static void main(String[] args) {
Map<String, Object> v1 = new HashMap<>();
v1.put("parents", new HashSet<>(Arrays.asList(new IdWrapper(Id.A), new IdWrapper(Id.B))));
v1.put("size", 1d);
Map<String, Object> v2 = new HashMap<>();
v2.put("parents", new HashSet<>(Arrays.asList(new IdWrapper(Id.B), new IdWrapper(Id.C))));
v2.put("size", 2d);
Map<String, Map<String, Object>> allVs = new HashMap<>();
allVs.put("v1", v1);
allVs.put("v2", v2);
上面代表了我正在处理的数据结构。我有一个外部映射(键类型无关紧要),它包含内部“属性映射”作为值。这些内部映射使用字符串来查找不同类型的数据。
在我处理的案例中,每个 v1、v2、... 代表一个“磁盘”。每个磁盘都有特定的大小,但可以有多个父级。
现在我需要将每个 parent Id 的大小加起来为Map<Id, Double>
.
对于上面的示例,该 map 将是 {B=3.0, A=1.0, C=2.0}
.
下面的代码给出了预期的结果:
HashMap<Id, DoubleAdder> adders = new HashMap<>();
allVs.values().forEach(m -> {
double size = (Double) m.get("size");
Set<IdWrapper> wrappedIds = (Set<IdWrapper>) m.get("parents");
wrappedIds.forEach(w -> adders.computeIfAbsent(w.getId(), a -> new DoubleAdder()).add(size));
});
System.out.println(adders.keySet().stream()
.collect(Collectors.toMap(Function.identity(), key -> adders.get(key).doubleValue())));
但是代码感觉很笨拙(比如我需要第二张 map 来计算尺寸)。
我有一个类似的情况,总是只有一个父级,并且可以使用
轻松解决collect(Collectors.groupingBy(...), Collectors.summingDouble(...);
但我对“多个” parent 的案件感到困惑。
所以,问题:上面的转换能否计算出所需的Map<Id, Double>
?使用 groupingBy()
重写?
仅作记录:以上内容只是我需要答案的问题的一个答案。我知道“数据布局”可能看起来很奇怪。实际上,我们实际上有不同的类来表示这些“磁盘”。但是我们的“框架”还允许使用此类 ID 和属性名称访问数据库中任何对象的属性。有时,当您遇到性能问题时,与访问真正的“磁盘”对象本身相比,以这种“原始属性映射”方式获取数据要快几个数量级。换句话说:我无法改变上下文的任何内容。我的问题只是关于重写该计算。
(我受限于 Java8 和“标准”Java 库,但对于较新版本的 Java 的附加答案或解决此问题的非标准方法也将受到赞赏)
最佳答案
这是一个单流管道解决方案:
Map<Id,Double> sums = allVs.values ()
.stream ()
.flatMap (m -> ((Set<IdWrapper>)m.get ("parents")).stream ()
.map (i -> new SimpleEntry<Id,Double>(i.getId(),(Double)m.get ("size"))))
.collect (Collectors.groupingBy (Map.Entry::getKey,
Collectors.summingDouble (Map.Entry::getValue)));
输出:
{B=3.0, A=1.0, C=2.0}
想法是将每个内部 Map
转换为条目的 Stream
,其中键是 Id
(“parents”Set
),值为对应的“size”。
然后很容易将 Stream
分组为所需的输出。
关于java - 有没有一种方法可以使用 "groupingBy"为嵌套结构中的多个元素收集 map ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53426852/