【问题标题】:Java groupingBy: Get two (or more) groups with single StreamJava groupingBy:使用单个 Stream 获取两个(或更多)组
【发布时间】:2020-01-07 02:27:58
【问题描述】:

如果我必须使用 Stream 根据两个不同的字段生成两个组,这是我可以采取的一种方法:

var prop1Group = beans.stream().collect(Collectors.groupingBy(Bean::getProp1));
var prop2Group = beans.stream().collect(Collectors.groupingBy(Bean::getProp2));

但是这种方法会遍历列表两次。 以一种命令式的方式,我可以在单次迭代中得到相同的结果,如下所示:

var prop1Group = new HashMap<String, Set<Bean>>();
var prop2Group = new HashMap<String, Set<Bean>>();

for (var bean : beans) {
    prop1Group.computeIfAbsent(bean.getProp1(), key -> new HashSet<>()).add(bean);
    prop2Group.computeIfAbsent(bean.getProp2(), key -> new HashSet<>()).add(bean);
}

有没有办法使用流以声明方式完成相同的工作,而不需要迭代两次?

【问题讨论】:

  • 在Java 12中,你可以使用the teeing collector,但是,我不认为“迭代两次”对于普通集合来说是一个实际的问题。这听起来更像是过早的优化。事实上,考虑到 JIT 和 Hotspot 优化器的工作方式,解决方案迭代两次但在内部循环中执行一次操作可能会更快。

标签: java java-8 functional-programming java-stream vavr


【解决方案1】:

既然您已将vavr 添加为标签,我还想添加另一个解决方案,即使用元组、模式匹配和折叠:

var result = list.foldLeft(
      Tuple.of(List.empty(), List.empty()),
      (lists, element) -> Match(element).of(
           Case($(Bean::getProp1), lists.map1(l -> l.append(element))),
           Case($(Bean::getProp2), lists.map2(l -> l.append(element)))
));

在这种情况下,组将存储为Tuple2 的字段。

【讨论】:

    【解决方案2】:

    根据@Holger 的评论,我可以像这样用 teeing 更加声明性地写它,但这仍然需要 2N

    beans.stream().collect(
                    Collectors.teeing(
                            groupingBy(Bean::getProp1),
                            groupingBy(Bean::getProp2),
                            List::of)) 
    

    【讨论】:

      猜你喜欢
      • 2017-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-21
      • 2022-12-04
      相关资源
      最近更新 更多