java - 有没有一种方法可以使用 "groupingBy"为嵌套结构中的多个元素收集 map ?

标签 java java-8 java-stream grouping

首先,一些上下文代码:

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/

相关文章:

java - 为什么 java 收集流对每个 getter 运行两次?

java - 在java中删除xml节点后,xml文件没有得到更新

java - Collection 中是否有一个对象不是静态的?

java - 如何在 Maven 中排除特定的单元测试

Java Stream - NullPointExeption when filter list

java - 是否可以模拟私有(private)方法的结果并同时获得声纳或 jacoco 的覆盖?

Java 8 Stream distinct 不起作用

Java流映射

Java GWT 依赖注入(inject)

java - 无法并行工作的自定义收集器