[英]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.