2016-11-04 2 views
8

Map<Double, 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; 
    } 

단위 테스트를 통해 사용 사례를 볼 수 있습니다.

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

당신은 코드에 대한 이러한 수입이 필요합니다 :

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

나는 그것이 도움이되기를 바랍니다

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

당신은 단위 테스트에 대한 이러한 수입이 필요합니다.

관련 문제