如何使用流平均大数?

我想采用以下方法:

public BigDecimal mean(List<BigDecimal> bigDecimals, RoundingMode roundingMode) {
    BigDecimal sum = BigDecimal.ZERO;
    int count=0;
    for(BigDecimal bigDecimal : bigDecimals) {
        if(null != bigDecimal) {
            sum = sum.add(bigDecimal);
            count++;
        }
    }
    return sum.divide(new BigDecimal(count), roundingMode);
}

并使用 Streams api 进行更新。以下是我到目前为止所得到的:

public BigDecimal average(List<BigDecimal> bigDecimals, RoundingMode roundingMode) {
    BigDecimal sum = bigDecimals.stream()
        .map(Objects::requireNonNull)
        .reduce(BigDecimal.ZERO, BigDecimal::add);
    long count = bigDecimals.stream().filter(Objects::nonNull).count();
    return sum.divide(new BigDecimal(count), roundingMode);
}

有没有办法在不流式传输两次(第二次获取计数)的情况下执行此操作?


答案 1
BigDecimal[] totalWithCount
                = bigDecimals.stream()
                .filter(bd -> bd != null)
                .map(bd -> new BigDecimal[]{bd, BigDecimal.ONE})
                .reduce((a, b) -> new BigDecimal[]{a[0].add(b[0]), a[1].add(BigDecimal.ONE)})
                .get();
BigDecimal mean = totalWithCount[0].divide(totalWithCount[1], roundingMode);

对于那些发现有帮助的代码的可选文本描述(如果您发现代码足够不言自明,请忽略):

  • 大数点列表将转换为流。
  • 空值将从流中筛选出来。
  • BigDecimals 的流被映射为 BigDecimal 的两个元素数组的流,其中第一个元素是原始流中的元素,第二个是值为 1 的占位符。
  • 在约化中,值在第一个元素中具有部分和,在第二个元素中具有部分计数。该元素的第一个元素包含要添加到总和中的每个 BigDecimal 值。的第二个元素未使用。a(a,b)bb
  • Reduce 返回一个可选选项,如果列表为空或仅包含 null 值,则该可选选项将为空。
    • 如果 Optional 不为空,Optional.get() 函数将返回 BigDecimal 的双元素数组,其中 BigDecimals 的总和位于第一个元素中,BigDecimals 的计数位于第二个元素中。
    • 如果 Optional 为空,则将抛出 NoSuchElementException。
  • 平均值的计算方法是将总和除以计数。

答案 2

您无需流式传输两次。只需调用 List.size() 进行计数:

public BigDecimal average(List<BigDecimal> bigDecimals, RoundingMode roundingMode) {
    BigDecimal sum = bigDecimals.stream()
        .map(Objects::requireNonNull)
        .reduce(BigDecimal.ZERO, BigDecimal::add);
    return sum.divide(new BigDecimal(bigDecimals.size()), roundingMode);
}