簡體   English   中英

計算對象的加權平均值 Java stream

[英]Calculate weighted average from objects with Java stream

給定一個簡單的Measurement值 class:

@Getter
@AllArgsConstructor
public class Measurement {
    private BigDecimal rawValue;
    private BigDecimal weighting;
}

該服務處理List<Measurement>並應通過應用以下步驟計算加權平均值:

  1. 將每個rawValue與其weighting相乘。
  2. 從 (1) 構建所有加權原始值的總和。
  3. 將總和 (2) 除以所有權重的總和。

例子

(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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM