Java 将嵌套集合值映射到聚合函数的流

Java stream to map nested collection values into aggregating function

@Data // lombok
public class Buzz {
  private String key;
  private Integer value;
  // many other fields
}

@Data // lombok
public class Fizz {
  private Long id;
  private String name;
  private List<Buzz> buzzes = Collections.emptyList();
  // many other fields here
}

此数据模型已设置,无法更改。

应用程序保证每个 Fizz 实例恰好有零 (0) 个或一 (1) 个 Buzz 实例具有键“points”。这意味着如果一个特定的 Fizz 实例在其列表中有 10,000 个 Buzz,则 一个且只有一个 Buzz 将具有“点”的键,或者 none 会。我永远不会有 2+ Buzzes 键为“points”。

我想遍历 fizzes 列表,如果 Buzz 有一个 key = "points",那么我想获取它的点值并将其与列表中的其他“点 Buzzes”相加嘶嘶声。如果 Buzz 键没有“点”键,我们可以将其值默认为零 (0)。

因此,如果有两个像这样的 Fizzes:

Fizz f1 = new Fizz();
Fizz f2 = new Fizz();
Fizz f3 = new Fizz();

Buzz b1 = new Buzz();
Buzz b2 = new Buzz();
Buzz b3 = new Buzz();
Buzz b4 = new Buzz();
Buzz b5 = new Buzz();
Buzz b6 = new Buzz();

b1.setKey("flim");
b1.setValue(-1);

b2.setKey("flim");
b2.setValue(25);

b3.setKey("points");
b3.setValue(10);

b4.setKey("points");
b4.setValue(18);

b5.setKey("flim");
b5.setValue(-7);

b6.setKey("flim");
b6.setValue(100);

f1.setBuzzes(Arrays.asList(b1, b2, b3));
f2.setBuzzes(Arrays.asList(b4, b5));
f3.setBuzzes(Arrays.asList(b6));

List<Fizz> fizzes = Arrays.asList(f1, f2, f3);

然后我正在寻找一个基于流的解决方案来计算所有“点数”。在这种情况下,最终值为 28,因为整个列表中只有 2 个 Buzze 的键 =“点”(b3b4),它们的总和为 10 + 18 = 28。

迄今为止我最好的尝试:

Integer pointsSum = fizzes.stream()
  .map(fizz -> fizz.getBuzzes().howDoItellIfItsBuzzHasAFizzKeyEqualToPoints())
  .reduce(Integer::sum)
  .orElse(0);

我的问题在于制作 map(...) 语句。我如何过滤以查看我们正在流式传输的 fizz 是否有一个 Buzz 其键等于“点”,以便我们可以映射到它的 Buzz value?

为此,您需要过滤 Buzz 个具有键 "points" 的对象。因为你有这个要求:

one and only one Buzz will have a key of "points", or none will

这意味着需要一个结果。为此 Stream IPA 提供了 findFirst() 方法,这是一个 short-circuit 操作(当它遇到第一个元素时它 returns 它并且不会处理流中的更多元素)。

请注意,结果可能会或可能不会出现在流中,因此 findFirst() returns 类型为 可选 的对象。 .map(Buzz::getValue) 将应用于 可选结果 但不会应用于流的元素。

            int pointsSum = fizzes.stream()
                .mapToInt(fizz -> getPoints(fizz))
                .sum();
    private static int getPoints(Fizz fizz) {
        return fizz.getBuzzes().stream()
                .filter(buzz -> buzz.getKey().equals("points"))
                .findFirst()
                .map(Buzz::getValue)
                .orElse(0);
    }

注:getPoints()的调用可以写成方法引用代替lambda ,但我 建议您通过将此方法中的所有代码放入 mapToInt() 来创建嵌套流。函数式编程在 Java 中引入, 而非 是为了编写复杂的实现,同时降低代码的复杂性。

    fizzes.stream().flatMap(t -> t.buzzes.stream())
    .filter(b -> b.getKey().equals("points"))
    .mapToInt(Buzz::getValue)
    .sum();