我有一个包含以下代码的小方法:

final int year = getYear();
final Carrier carrier = getCarrier();
final CarrierMetrics metrics = new CarrierMetrics(carrier);
repository.getFlightStream(year)
          .filter(flight -> flight.getCarrier().equals(carrier))
          .forEach(flight -> {
             metrics.addFlight(flight);
             printf("%,10d\t%,10d\t%,10d\t%,10d\r",
                    metrics.getTotalFlights(),
                    metrics.getTotalCancelled(),
                    metrics.getTotalDiverted(),
                    metrics.getAirports().size()
             );
          });


希望很明显,我正在做的是在处理流中的每个Flight时累积指标。该代码确实有效,但我想知道是否有更好的(功能更强大)的方式来实现此行为,可能使用收集器。任何反馈表示赞赏。

谢谢,

-托尼

最佳答案

如果forEach中的打印很重要,
那么您当前的解决方案是好的。
forEach是为副作用而设计的,
并且有两个副作用:将指标添加到CarrierMetrics实例并进行打印。

如果forEach中的打印仅用于调试,
而不是最终解决方案中的目的,
那么更实用的实现是将结果直接收集到CarrierMetrics实例中,
而不是先初始化实例并使用forEach手动添加。
您可以使用带有3个参数的collect(...)重载:


Supplier<CarrierMetrics>创建初始CarrierMetrics实例,该实例将用作累加器
BiConsumer<CarrierMetrics, Flight>实例传递给累加器的Flight


类型Flight只是基于您共享的代码的猜测。这是流的类型(也是CarrierMetrics.addFlight方法的参数的类型)

在并行流的情况下将多个累加器组合在一起的BiConsumer<CarrierMetrics, CarrierMetrics>


像这样:

final int year = getYear();
final CarrierMetrics metrics = repository.getFlightStream(year)
      .filter(flight -> flight.getCarrier().equals(carrier))
      .collect(CarrierMetrics::new, CarrierMetrics::addFlight, (a1, a2) -> {});


第三个参数,合并器,是一个虚拟的,
您将需要解决该问题。
它的实现应将两个CarrierMetrics参数组合为第一个。
(我无法举一个具体的例子,因为您还没有分享足够的有关CarrierMetrics的详细信息,以便能够看到如何做。
但是举个例子,在List累加器的情况下,
实现可以是(a1, a2) -> a1.addAll(a2)。)

(最后,此示例假定CarrierMetrics具有无参数构造函数,以使CarrierMetrics::new引用起作用。
如果没有这样的构造函数,则可以使用适当的lambda表达式,例如() -> new CarrierMetrics(...)。)

09-10 07:11