使用Java 8流计算加权平均值

Viv*_*thi 10 java java-8 java-stream

如何计算加权平均值,Map<Double, Integer>其中整数值是要平均的Double值的权重.例如:Map有以下元素:

  1. (0.7,100)//值为0.7,权重为100
  2. (0.5,200)
  3. (0.3,300)
  4. (0.0,400)

我希望使用Java 8流应用以下公式,但不确定如何一起计算分子和分母并同时保留它.如何在这里使用减少?

在此输入图像描述

Tun*_*aki 16

您可以为此任务创建自己的收集器:

static <T> Collector<T,?,Double> averagingWeighted(ToDoubleFunction<T> valueFunction, ToIntFunction<T> weightFunction) {
    class Box {
        double num = 0;
        long denom = 0;
    }
    return Collector.of(
             Box::new,
             (b, e) -> { 
                 b.num += valueFunction.applyAsDouble(e) * weightFunction.applyAsInt(e); 
                 b.denom += weightFunction.applyAsInt(e);
             },
             (b1, b2) -> { b1.num += b2.num; b1.denom += b2.denom; return b1; },
             b -> b.num / b.denom
           );
}
Run Code Online (Sandbox Code Playgroud)

此自定义收集器将两个函数作为参数:一个函数返回用于给定流元素的值(作为a ToDoubleFunction),另一个函数返回权重(作为a ToIntFunction).它使用一个辅助本地类,在收集过程中存储分子和分母.每次接受一个条目时,分子会增加,其结果是将该值与其权重相乘,分母随着权重而增加.然后,终结者将两者的分割作为a Double.

示例用法是:

Map<Double,Integer> map = new HashMap<>();
map.put(0.7, 100);
map.put(0.5, 200);

double weightedAverage =
  map.entrySet().stream().collect(averagingWeighted(Map.Entry::getKey, Map.Entry::getValue));
Run Code Online (Sandbox Code Playgroud)