Java stream.mapToDouble.sum的精度问题

Java 里的 double 类型的精度问题,是一个老生常谈的问题了,现在我们在 Java 8 中通常用 BigDecimal 来解决这个问题,但最近在做一些功能时还是碰到了 double 的精度带来的一些困扰。

先上一段伪代码

1
2
3
4
5
6
7
8
@Data
class Bean {
private BigDecimal amount;
}

void test(List<Bean> beanList) {
BigDecimal sumAmount = BigDecimal.valueOf(beanList.stream().mapToDouble(bean -> bean.getAmount().doubleValue()).sum());
}

此时将 Bean.amount 转成 doublesum 操作后,sumAmount 出现了精度问题。

但由于 stream() 并没有提供直接操作 BigDecimal 的方法,那么有没有办法简单地将这个精度问题解决掉呢?还是有的。

使用 reduce() 方法,这个方法是 Stream 的一个聚合方法,可以把 Stream 中的所有元素按照聚合函数聚合成一个结果。

按照这个思路进行修改后的伪代码

1
2
3
4
5
6
7
8
@Data
class Bean {
private BigDecimal amount;
}

void test(List<Bean> beanList) {
BigDecimal sumAmount = beanList.stream().map(Bean::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
}

首先使用 map() 将需要操作的元素拿出来,然后通过 reduce() 来使用 BigDecimaladd() 方法来实现累加。

reduce() 有三个 override 实现方法:

  • T reduce(T identity, BinaryOperator<T> accumulator);
  • Optional<T> reduce(BinaryOperator<T> accumulator);
  • <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);

这里我们使用的是第一个,identity 是用于执行 accumulator 的初始值,如果 list 是空值,那么就会直接返回 indentity 的值。