Java 使用Streams API收集器平均大小数

Java 使用Streams API收集器平均大小数,java,java-8,java-stream,average,bigdecimal,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

目前的方法是基于双重产品奖

public Map<String, BigDecimal> averageProductPriceInCategory() {

    return shopping.entrySet()
            .stream()
            .flatMap(e -> e.getValue().keySet().stream())
            .collect(Collectors.groupingBy(Product::getCategory,
                    Collectors.averagingDouble(Product::getPrize)));
}
public Map averageProductPriceInCategory(){
return shopping.entrySet()
.stream()
.flatMap(e->e.getValue().keySet().stream())
.collect(收集器).groupingBy(产品::getCategory,
收藏家。平均双倍(产品::getPrize));
}
购物基本上是一张地图:
地图

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

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

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))));
Map totalProductPriceInEachCategory=shopping.entrySet().stream()
.flatMap(e->e.getValue().keySet().stream())
.collect(收集器).groupingBy(产品::getCategory,
Collectors.mapping(产品::getPrize,
减少(BigDecimal.ZERO,BigDecimal::add));
看看or是如何实现的

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);
}

为了便于理解,我将操作分解为两个步骤。如果您愿意,可以将这两个步骤结合起来

    Map<String, BigDecimal[]> stringMap = shopping.entrySet()
            .stream()
            .flatMap(e -> e.getValue().keySet().stream())
            .collect(Collectors.groupingBy(Product::getCategory,Collectors.collectingAndThen(Collectors.toList(),l -> l.stream().map(Product::getPrize)
                    .map(bd -> new BigDecimal[]{bd, BigDecimal.ONE})
                    .reduce((a, b) -> new BigDecimal[]{a[0].add(b[0]), a[1].add(BigDecimal.ONE)})
                    .get()
            )));

    Map<String, BigDecimal> stringBigDecimalMap = stringMap.entrySet().stream()
            .collect(Collectors.toMap(Map.Entry::getKey,e -> e.getValue()[0].divide(e.getValue()[1])));
Map-stringMap=shopping.entrySet()
.stream()
.flatMap(e->e.getValue().keySet().stream())
.collect(Collectors.groupingBy)(产品::getCategory,Collectors.collectingAndThen(Collectors.toList(),l->l.stream().map)(产品::getPrize)
.map(bd->new BigDecimal[]{bd,BigDecimal.ONE})
.reduce((a,b)->新的BigDecimal[]{a[0]。添加(b[0]),a[1]。添加(BigDecimal.ONE)})
.get()
)));
Map stringBigDecimalMap=stringMap.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey,e->e.getValue()[0]。divide(e.getValue()[1]));
说明:

  • 在第一个操作中,分组后,BigDecimals流被映射为BigDecimal的两个元素数组的流,其中第一个元素是原始流中的元素,第二个元素是值为1的占位符
  • 在减少
    (a,b)
    值的
    a
    值中,第一个元素中的部分和和以及第二个元素中的部分计数。
    b
    元素的第一个元素包含每个要添加到总和的BigDecimal值。未使用
    b
    的第二个元素
  • Reduce返回一个可选值,如果列表为空或仅包含null值,则该值将为空。
    • 如果Optional不是空的,Optional.get()函数将返回一个由两个元素组成的BigDecimal数组,其中BigDecimal的和在第一个元素中,BigDecimal的计数在第二个元素中
    • 如果可选项为空,将抛出NoTouchElementException
  • 通过将总和除以计数来计算平均值。这是对中间映射中的每个条目执行的
    map stringMap

这是基于
[Double | Int]Pipeline.average()
的源代码。它使用数组存储项目计数(在索引
0
)和总和(在索引
1

public Map averageProductPriceInCategory(){
return shopping.entrySet().stream()
.flatMap(entry->entry.getValue().keySet().stream())
.collect(收集器.groupingBy(
产品:getCategory,
收藏(
()->新的BigDecimal[]{BigDecimal.ZERO,BigDecimal.ZERO},
(阵列、产品)->{
数组[0]=数组[0]。添加(BigDecimal.ONE);
数组[1]=数组[1]。添加(product.getPrice());
},
(左,右)->{
左[0]=左[0]。添加(右[0]);
左[1]=左[1]。添加(右[1]);
左转;
},

数组->数组[0]。与(BigDecimal.ONE)相比,您可以创建自己的收集器,如下所示:

Collector<BigDecimal, BigDecimal[], BigDecimal> avgCollector = Collector.of(
      () -> new BigDecimal[]{BigDecimal.ZERO, BigDecimal.ZERO},
      (pair, val) -> {
        pair[0] = pair[0].add(val);
        pair[1] = pair[1].add(BigDecimal.ONE);
      },
      (pair1, pair2) -> new BigDecimal[]{pair1[0].add(pair2[0]), pair1[1].add(pair2[1])},
      (pair) -> pair[0].divide(pair[1], 2, RoundingMode.HALF_UP)
);
收集器avgCollector=Collector.of( ()->新的BigDecimal[]{BigDecimal.ZERO,BigDecimal.ZERO}, (配对,val)->{ 对[0]=对[0]。添加(val); 对[1]=对[1]。添加(BigDecimal.ONE); }, (pair1,pair2)->新的BigDecimal[]{pair1[0]。添加(pair2[0]),pair1[1]。添加(pair2[1]), (对)->对[0]。分割(对[1],2,取整模式。对半向上) );
…然后使用它:

Map<String, BigDecimal> totalProductPriceInEachCategory = shopping.values().stream()
      .flatMap(e -> e.keySet().stream())
      .collect(groupingBy(Product::getCategory, mapping(Product::getPrice, avgCollector)));
Map totalProductPricineAchCategory=shopping.values().stream()
.flatMap(e->e.keySet().stream())
.collect(分组依据(产品::getCategory,映射(产品::getPrice,avgCollector));

Map>-键表示产品类别-值(BigDecimal)代表指定类别中的平均产品奖或使用更广泛的解决方案,
…stream.collect(BigDecimalSummaryStatistics.statistics()).getAverage(MathContext.DECIMAL128))
Collector<BigDecimal, BigDecimal[], BigDecimal> avgCollector = Collector.of(
      () -> new BigDecimal[]{BigDecimal.ZERO, BigDecimal.ZERO},
      (pair, val) -> {
        pair[0] = pair[0].add(val);
        pair[1] = pair[1].add(BigDecimal.ONE);
      },
      (pair1, pair2) -> new BigDecimal[]{pair1[0].add(pair2[0]), pair1[1].add(pair2[1])},
      (pair) -> pair[0].divide(pair[1], 2, RoundingMode.HALF_UP)
);
Map<String, BigDecimal> totalProductPriceInEachCategory = shopping.values().stream()
      .flatMap(e -> e.keySet().stream())
      .collect(groupingBy(Product::getCategory, mapping(Product::getPrice, avgCollector)));