我有一份BigDecimals的名单。
List<BigDecimal> amounts = new ArrayList<>()如何使用Java8 streams获得上述列表的汇总统计数据,同时又不会损失BigDecimal小数位3-4位的精度?
发布于 2018-08-02 19:30:38
我创建了this answer的通用摘要统计信息收集器的BigDecimal专门化,允许对其进行扩展以支持求和,因此还可以计算平均值:
/**
* Like {@code DoubleSummaryStatistics}, {@code IntSummaryStatistics}, and
* {@code LongSummaryStatistics}, but for {@link BigDecimal}.
*/
public class BigDecimalSummaryStatistics implements Consumer<BigDecimal> {
public static Collector<BigDecimal,?,BigDecimalSummaryStatistics> statistics() {
return Collector.of(BigDecimalSummaryStatistics::new,
BigDecimalSummaryStatistics::accept, BigDecimalSummaryStatistics::merge);
}
private BigDecimal sum = BigDecimal.ZERO, min, max;
private long count;
public void accept(BigDecimal t) {
if(count == 0) {
Objects.requireNonNull(t);
count = 1;
sum = t;
min = t;
max = t;
}
else {
sum = sum.add(t);
if(min.compareTo(t) > 0) min = t;
if(max.compareTo(t) < 0) max = t;
count++;
}
}
public BigDecimalSummaryStatistics merge(BigDecimalSummaryStatistics s) {
if(s.count > 0) {
if(count == 0) {
count = s.count;
sum = s.sum;
min = s.min;
max = s.max;
}
else {
sum = sum.add(s.sum);
if(min.compareTo(s.min) > 0) min = s.min;
if(max.compareTo(s.max) < 0) max = s.max;
count += s.count;
}
}
return this;
}
public long getCount() {
return count;
}
public BigDecimal getSum()
{
return sum;
}
public BigDecimal getAverage(MathContext mc)
{
return count < 2? sum: sum.divide(BigDecimal.valueOf(count), mc);
}
public BigDecimal getMin() {
return min;
}
public BigDecimal getMax() {
return max;
}
@Override
public String toString() {
return count == 0? "empty": (count+" elements between "+min+" and "+max+", sum="+sum);
}
}它可以使用类似于DoubleSummaryStatistics的对等物,例如
BigDecimalSummaryStatistics bds = list.stream().collect(BigDecimalSummaryStatistics.statistics());作为完整的示例:
List<BigDecimal> list = Arrays.asList(BigDecimal.ZERO, BigDecimal.valueOf(-2), BigDecimal.ONE);
BigDecimalSummaryStatistics bds = list.stream().collect(BigDecimalSummaryStatistics.statistics());
System.out.println(bds);
System.out.println("average: "+bds.getAverage(MathContext.DECIMAL128));3 elements between -2 and 1, sum=-1
average: -0.3333333333333333333333333333333333发布于 2019-06-26 07:35:21
您可以将Java Streams与具有BigDecimalSummaryStatistics类的Eclipse Collections一起使用:
List<BigDecimal> amounts =
Lists.mutable.with(BigDecimal.ONE, BigDecimal.TEN, BigDecimal.ZERO, BigDecimal.ONE);
BigDecimalSummaryStatistics stats =
amounts.stream().collect(Collectors2.summarizingBigDecimal(each -> each));
Assert.assertEquals(BigDecimal.ZERO, stats.getMin());
Assert.assertEquals(BigDecimal.TEN, stats.getMax());
Assert.assertEquals(BigDecimal.valueOf(12L), stats.getSum());
Assert.assertEquals(BigDecimal.valueOf(3L), stats.getAverage());
Assert.assertEquals(4L, stats.getCount());注意:我是Eclipse Collections的提交者
发布于 2018-08-02 17:21:11
如果您愿意使用第三方库(与Java8Streams兼容),则可以使用jOOλ,您可以使用它来编写:
Tuple5<
Long,
Optional<BigDecimal>,
Optional<BigDecimal>,
Optional<BigDecimal>,
Optional<BigDecimal>
> tuple
amounts.stream()
.collect(Tuple.collectors(
Agg.sum(),
Agg.count(),
Agg.avg(),
Agg.<BigDecimal>min(),
Agg.<BigDecimal>max()
));这不会导致精度损失,但可能比聚合双精度要慢得多。
https://stackoverflow.com/questions/51645432
复制相似问题