Thursday, 8 January 2015

Java 8 Lambdas: Map Reduce the correct way to sum a stream of BigDecimals

Let's start with a simple HashMap of Strings to Doubles as below and put in some values.
Map<String, Double> map = new HashMap<>();
map.put("one", 1.0);
map.put("two", 2.0);
map.put("three", 3.0);



Finding the sum using Java 8 lambdas is pretty straight forward
double sum = map.entrySet().stream().mapToDouble
   (e->e.getValue()).sum();

or using method references
double sum = map.entrySet().stream().mapToDouble
   (Map.Entry::getValue).sum();
What if you wanted to do the same thing for a map of String to BigDecimals?
Not being from a functional background my first instinct (based on the example above) was to do this:
Map<String, BigDecimal> map = new HashMap<>();
map.put("one", BigDecimal.ONE);
map.put("two", BigDecimal.valueOf(2));
map.put("three", BigDecimal.valueOf(3));
BigDecimal sum = BigDecimal.valueOf(map.entrySet().stream().mapToDouble(e->e.getValue().doubleValue()).sum());//DON'T DO THIS!!
This code is hideous, not to mention wrong because you are not actually using the add 
method of the BigDecimal which might be important especially if you are multiplying and 
dividing the values in the stream.
The correct way to sum this stream of BigDecimals is to use the map reduce pattern:
BigDecimal sum = map.entrySet().stream().map
   (e->e.getValue()).reduce(BigDecimal.ZERO, BigDecimal::add);
This is much more elegant.  It does exactly what you want by using the method add on 
the BigDecimal and not just re-creating a BigDecimal after doing the work on doubles.

1 comment: