[英]Calculate weighted average from objects with Java stream
给定一个简单的Measurement
值 class:
@Getter
@AllArgsConstructor
public class Measurement {
private BigDecimal rawValue;
private BigDecimal weighting;
}
该服务处理List<Measurement>
并应通过应用以下步骤计算加权平均值:
rawValue
与其weighting
相乘。例子
(1.0 * 5.0) + (2.0 * 2.0) + (3.0 * 7.0) / 14
这是我当前的实现:
public class MeasurementService {
private List<Measurement> measurements = new ArrayList<>();
public MeasurementService() {
Measurement m1 = new Measurement(new BigDecimal("1.0"), new BigDecimal("5.0"));
Measurement m2 = new Measurement(new BigDecimal("2.0"), new BigDecimal("2.0"));
Measurement m3 = new Measurement(new BigDecimal("3.0"), new BigDecimal("7.0"));
measurements.add(m1);
measurements.add(m2);
measurements.add(m3);
}
public BigDecimal calculateWeightedAverage() {
final BigDecimal totalWeighting = measurements.stream()
.map(measurement -> measurement.getWeighting())
.reduce(BigDecimal.ZERO, BigDecimal::add);
return measurements.stream()
.map(measurement -> measurement.getRawValue().multiply(measurement.getWeighting()))
.reduce(BigDecimal.ZERO, BigDecimal::add)
.divide(totalWeighting, 3, RoundingMode.DOWN);
}
}
虽然这是有效的,但我不喜欢两次使用 stream measurements
,我敢打赌有更好的方法。 不幸的是,到目前为止我还想不出一个。
您知道如何简化calculateWeightedAverage
方法吗?
我正在使用 Java 17。
假设您使用的是 Java 12 或更高版本,为了避免 stream 超过列表两次,您可以使用发球收集器。 除了这里的文档之外,还有一篇关于它的文章the-teeing-collector
public BigDecimal calculateWeightedAverage() {
return measurements.stream()
.collect(Collectors.teeing(
Collectors.reducing(BigDecimal.ZERO, m -> m.getRawValue().multiply(m.getWeighting()), BigDecimal::add),
Collectors.reducing(BigDecimal.ZERO, Measurement::getWeighting, BigDecimal::add ),
(weightedRawValues , totalWeighting) -> weightedRawValues.divide(totalWeighting, 3, RoundingMode.DOWN)));
}
Map 到一个新的 class 并将其减少,然后使用减少的值来计算最终结果。 这适用于 Java 8+。 就像是:
@Data
@RequiredArgsConstructor
public class ResultAccumulator {
public static final ResultAccumulator ZERO = new ResultAccumulator(BigDecimal.ZERO, BigDecimal.ZERO);
private final BigDecimal weightedValue;
private final BigDecimal weightSum;
public static ResultAccumulator fromMeasurement(final Measurement measurement) {
return new ResultAccumulator(
measurement.getRawValue().multiply(measurement.getWeighting()),
measurement.getWeighting());
}
public ResultAccumulator merge(final ResultAccumulator other) {
return new ResultAccumulator(
getWeightedValue().add(other.getWeightedValue()),
getWeightSum().add(other.getWeightSum()));
}
public BigDecimal getFinalResult() {
return getWeightedValue().divide(getWeightSum(), 3, RoundingMode.DOWN);
}
}
public BigDecimal calculateWeightedAverage() {
final ResultAccumulator result = measurements.stream()
.map(ResultAccumulator::fromMeasurement)
.reduce(
ResultAccumulator.ZERO,
ResultAccumulator::merge);
return result.getFinalResult();
}
或者通过将“加权值”存储在rawValue
中并将权重总和存储在weight
中来快速而肮脏地进行操作并减少原始Measurement
值。 两个类的结构是相同的,但是使用两个类在 2 周内对他人和你自己都不会造成混淆。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.