2016-11-04 130 views
8

如何計算Map<Double, Integer>的加權平均值,其中Integer值是Double值的平均權重。 例如:地圖具有以下要素:使用Java 8流計算加權平均值

  1. (0.7,100)//值爲0.7和重量爲100
  2. (0.5,200)
  3. (0.3,300)
  4. (0.0, 400)

我正在尋找使用Java 8流應用以下公式,但不確定如何計算分子和分母在一起並同時保留它。如何在這裏使用減少?

enter image description here

+3

您是否檢查了['Collectors.averagingDouble'](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#averagingDouble-java.util.function .ToDoubleFunction-)方法? – Aaron

回答

9

您可以創建自己的收集完成這個任務:

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

這種風俗收藏家有兩個功能參數:一個是返回值要用於給定的流元的函數(作爲ToDoubleFunction),另一個返回權重(作爲ToIntFunction)。它使用一個輔助本地類來存儲收集過程中的分子和分母。每次輸入被接受時,分子都會隨着它的權重乘以結果而增加,並且分母隨着權重而增加。終結者然後將兩者的劃分返回爲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)); 
+0

謝謝你的這個精彩的解釋。我會閱讀更多關於定製收藏家的信息。 –

0

可以使用此過程來計算的映射的加權平均。請注意,地圖條目的關鍵字應該包含該值,而地圖條目的值應該包含重量。

 /** 
    * Calculates the weighted average of a map. 
    * 
    * @throws ArithmeticException If divide by zero happens 
    * @param map A map of values and weights 
    * @return The weighted average of the map 
    */ 
    static Double calculateWeightedAverage(Map<Double, Integer> map) throws ArithmeticException { 
     double num = 0; 
     double denom = 0; 
     for (Map.Entry<Double, Integer> entry : map.entrySet()) { 
      num += entry.getKey() * entry.getValue(); 
      denom += entry.getValue(); 
     } 

     return num/denom; 
    } 

你可以看看它的單元測試看看一個用例。

 /** 
    * Tests our method to calculate the weighted average. 
    */ 
    @Test 
    public void testAveragingWeighted() { 
     Map<Double, Integer> map = new HashMap<>(); 
     map.put(0.7, 100); 
     map.put(0.5, 200); 
     Double weightedAverage = calculateWeightedAverage(map); 
     Assert.assertTrue(weightedAverage.equals(0.5666666666666667)); 
    } 

您需要這些進口的單元測試:

import org.junit.Assert; 
import org.junit.Test; 

您需要這些進口代碼:

import java.util.HashMap; 
import java.util.Map; 

我希望它能幫助。