java - 使用 Streams API 收集器平均 BigDecimals

标签 java java-8 java-stream average bigdecimal

当前的方法基于双倍类型的产品奖励。

public Map<String, BigDecimal> averageProductPriceInCategory() {

    return shopping.entrySet()
            .stream()
            .flatMap(e -> e.getValue().keySet().stream())
            .collect(Collectors.groupingBy(Product::getCategory,
                    Collectors.averagingDouble(Product::getPrize)));
}

购物基本上是一张 map :Map<Client, Map<Product,Integer>> ,

  • 外键代表客户端
  • 内部 key 代表产品。产品类成员是名称、类别、价格(以前为 double 类型) - 希望将提供的代码重构为使用价格作为 BigDecimal 类型的代码
  • 内部映射值(整数)代表属于特定客户的指定产品的数量

以下代码片段只能用于计算属于指定类别的产品的总奖金。不确定如何使用 BigDecimals 计算类别的平均产品奖

Map<String, BigDecimal> totalProductPriceInEachCategory = shopping.entrySet().stream()
                .flatMap(e -> e.getValue().keySet().stream())
                .collect(Collectors.groupingBy(Product::getCategory,
                        Collectors.mapping(Product::getPrize,
                                Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))));

最佳答案

看看如何 Collectors.averagingDouble Collectors.averagingInt 已实现。

public static <T> Collector<T, ?, Double>
averagingInt(ToIntFunction<? super T> mapper) {
    return new CollectorImpl<>(
            () -> new long[2],
            (a, t) -> { a[0] += mapper.applyAsInt(t); a[1]++; },
            (a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; },
            a -> (a[1] == 0) ? 0.0d : (double) a[0] / a[1], CH_NOID);
}

本质上,您需要一个可变的累积类型来保存 BigDecimal这是产品价格的总和,以及 int这是经过加工的一些产品。有了这个,问题就归结为编写一个简单的 Collector<Product, AccumulationType, BigDecimal> .

我简化了一个示例并删除了 getter/setter 和全参数构造函数。而不是嵌套类 ProductPriceSummary ,您可以对 2 个元素使用任何可变持有者类。

class AverageProductPriceCollector implements Collector<Product, AverageProductPriceCollector.ProductPriceSummary, BigDecimal> {

    static class ProductPriceSummary {

        private BigDecimal sum = BigDecimal.ZERO;
        private int n;

    }

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

    @Override
    public BiConsumer<ProductPriceSummary, Product> accumulator() {
        return (a, p) -> {
            // if getPrize() still returns double
            // a.sum = a.sum.add(BigDecimal.valueOf(p.getPrize()));

            a.sum = a.sum.add(p.getPrize());
            a.n += 1;
        };
    }

    @Override
    public BinaryOperator<ProductPriceSummary> combiner() {
        return (a, b) -> {
            ProductPriceSummary s = new ProductPriceSummary();
            s.sum = a.sum.add(b.sum);
            s.n = a.n + b.n;

            return s;
        };
    }

    @Override
    public Function<ProductPriceSummary, BigDecimal> finisher() {
        return s -> s.n == 0 ?
                   BigDecimal.ZERO :
                   s.sum.divide(BigDecimal.valueOf(s.n), RoundingMode.CEILING);
    }

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

}

关于java - 使用 Streams API 收集器平均 BigDecimals,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54278847/

相关文章:

Java Streams : Do f() for N - 1 elements and g() for N. 即最后一个元素的不同函数

java - 将 Java Future 转换为 CompletableFuture

java - 使用重复的对象流式传输distinct()

Java并发执行同一个函数

java - 在默认文本编辑器中打开一个文本文件...通过 Java?

java - 是否可以在 jsp 中根据条件设置页面内容类型或为单个 jsp 设置不同的内容类型

java - Java 8 Lambda 上的反射类型推断

java - 为什么发出终端操作后 Java close() 不流式传输?

java - 在Java8中使用过滤器和映射提取数据库列数据

java - Android RunTimeException 无法实例化类