如何使用Streams平均BigDecimals?

Pat*_*ner 22 java collections bigdecimal java-8 java-stream

我想采取以下方法:

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);
}
Run Code Online (Sandbox Code Playgroud)

并使用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);
}
Run Code Online (Sandbox Code Playgroud)

有没有办法在没有两次流式传输的情况下做到这一点(第二次获得计数)?

Wil*_*ord 18

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);
Run Code Online (Sandbox Code Playgroud)

找到有用的代码的可选文本描述(如果您发现代码足够自我解释,请忽略).

  • BigDecimals列表将转换为流.
  • 从流中过滤掉空值.
  • BigDecimals流被映射为BigDecimal的两个元素数组的流,其中第一个元素是原始流中的元素,第二个元素是值为1的占位符.
  • 在reduce中a,(a,b)value具有第一个元素中的部分和和第二个元素中的部分计数.元素的第一个b元素包含要添加到总和的每个BigDecimal值.b没有使用第二个元素.
  • 如果列表为空或仅包含空值,则Reduce返回一个可选的空值.
    • 如果Optional不为空,则Optional.get()函数将返回BigDecimal的两个元素数组,其中BigDecimals的总和位于第一个元素中,BigDecimals的计数位于第二个元素中.
    • 如果Optional为空,则抛出NoSuchElementException.
  • 通过将总和除以计数来计算平均值.


xeh*_*puk 13

您不需要流两次.只需要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);
}
Run Code Online (Sandbox Code Playgroud)

  • @TeodorMarinescu 对于这种情况,可以返回 `Optional&lt;BigDecimal&gt;`。我认为抛出的“ArithmeticException”也很好。 (2认同)

Geo*_*lou 6

我使用上述方法来获取 BigDecimal 对象列表的平均值。该列表允许空值。

public BigDecimal bigDecimalAverage(List<BigDecimal> bigDecimalList, RoundingMode roundingMode) {
    // Filter the list removing null values
    List<BigDecimal> bigDecimals = bigDecimalList.stream().filter(Objects::nonNull).collect(Collectors.toList());

    // Special cases
    if (bigDecimals.isEmpty())
        return null;
    if (bigDecimals.size() == 1)
        return bigDecimals.get(0);

    // Return the average of the BigDecimals in the list
    return bigDecimals.stream().reduce(BigDecimal.ZERO, BigDecimal::add).divide(new BigDecimal(bigDecimals.size()), roundingMode);
}
Run Code Online (Sandbox Code Playgroud)


Nov*_*voj 5

或者,您可以使用此收集器实现:

class BigDecimalAverageCollector implements Collector<BigDecimal, BigDecimalAccumulator, BigDecimal> {

    @Override
    public Supplier<BigDecimalAccumulator> supplier() {
        return BigDecimalAccumulator::new;
    }

    @Override
    public BiConsumer<BigDecimalAccumulator, BigDecimal> accumulator() {
        return BigDecimalAccumulator::add;
    }

    @Override
    public BinaryOperator<BigDecimalAccumulator> combiner() {
        return BigDecimalAccumulator::combine;
    }

    @Override
    public Function<BigDecimalAccumulator, BigDecimal> finisher() {
        return BigDecimalAccumulator::getAverage;
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Collections.emptySet();
    }

    @NoArgsConstructor
    @AllArgsConstructor
    static class BigDecimalAccumulator {
        @Getter private BigDecimal sum = BigDecimal.ZERO;
        @Getter private BigDecimal count = BigDecimal.ZERO;

        BigDecimal getAverage() {
           return BigDecimal.ZERO.compareTo(count) == 0 ?
                  BigDecimal.ZERO :
                  sum.divide(count, 2, BigDecimal.ROUND_HALF_UP);
        }

        BigDecimalAccumulator combine(BigDecimalAccumulator another) {
            return new BigDecimalAccumulator(
                    sum.add(another.getSum()),
                    count.add(another.getCount())
            );
        }

        void add(BigDecimal successRate) {
            count = count.add(BigDecimal.ONE);
            sum = sum.add(successRate);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

并使用它:

BigDecimal mean = bigDecimals.stream().collect(new BigDecimalAverageCollector());
Run Code Online (Sandbox Code Playgroud)

注意:示例使用Project Lombok注释来缩短粘合代码.