当前位置: 首页 > 知识库问答 >
问题:

使用Streams API收集器对BigDecimal进行平均

姬阳曜
2023-03-14

目前的方法基于双类型的产品奖赏。

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 >

  • 外部键表示客户端
  • 内部键代表产品。Product类成员为name、category、price(以前为double类型)-希望将提供的代码重构为一个,使用price作为bigdecimal
  • 类型
  • 内部映射值(整数)表示属于特定客户端的指定产品的数量
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))));

共有1个答案

子车高歌
2023-03-14

看看collectors.averagingdoublecollectors.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(已处理的产品数量)。这样,问题就归结为编写一个简单的收集器

我简化了一个示例,删除了getter/setters和一个全参数构造函数。您可以不使用嵌套类ProductpriceSummary,而是为2个元素使用任何可变的holder类。

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为它创建实体类 其中类名为 在运行API时,我得到的响应是 我的问题。 我在MongoDB中设计集合的方式是否有问题。我假设我的用例是引入对象类型的原因。 如何在java(即@document)中映射此集合。我错过了什么?在类名中是否还有其他需要配置的内容

  • 我当前的尝试基于double type类成员: 外部键表示客户端 内部密钥表示产品 内部映射值(整数)表示属于特定客户端的指定产品的数量 Product类成员是name、category、price(以前是double类型)-想要将提供的代码重构为一个,使用price作为BigDecimal类型 我如何使这段代码也适用于BigDecimals? 仍然想知道是否可以在不使用之前使用进行重构?

  • 问题内容: 我有以下课程。 我希望能够按年龄分组,然后收集人员名称列表,而不是人员对象本身。全部以一个漂亮的lamba表达式表示。 为了简化所有步骤,我链接了当前的解决方案,该解决方案按年龄存储分组的结果,然后对其进行迭代以收集名称。 当前解决方案 不理想,为了学习,我想有一个更优雅,更有效的解决方案。 问题答案: 将Stream与分组时,可以使用自定义对值指定归约运算。在这里,我们需要使用,它需

  • 输入: null 目标是使以下语句可编译: 这里有什么用?它想让我<编码>匹配器<?super java.util.list >并告诉我通过了 。那么如何在这里传递匹配器集合呢? 有一个关于将集合与hamcrest进行比较的问题,但没有传递Matchers集合的示例,而不是元素。

  • 问题内容: 我上这堂课 然后,我有该类对象的列表。 有没有一种方法创建一个包含from 和as值的索引? 到目前为止,这是我尝试过的。 但这给我一个,我想要一个。 我已经尝试过使用方法-它可以工作-但我确定必须有一种方法可以在一个衬里中进行操作- 或者至少仅使用和方法 问题答案: 似乎没有平面映射收集器可在Java8中用作下游,但已被Java9提出并接受:https ://bugs.openjdk

  • 我有一个地图列表,并希望对某些列进行分组和求和。 地图列表: