Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to average BigDecimals using Streams?

I'm wanting to take the following method:

public BigDecimal mean(List<BigDecimal> bigDecimals, RoundingMode roundingMode) {     BigDecimal sum = BigDecimal.ZERO;     int count=0;     for(BigDecimal bigDecimal : bigDecimals) {         if(null != bigDecimal) {             sum = sum.add(bigDecimal);             count++;         }     }     return sum.divide(new BigDecimal(count), roundingMode); } 

and update it using the Streams api. Here's what I've got thus far:

public BigDecimal average(List<BigDecimal> bigDecimals, RoundingMode roundingMode) {     BigDecimal sum = bigDecimals.stream()         .map(Objects::requireNonNull)         .reduce(BigDecimal.ZERO, BigDecimal::add);     long count = bigDecimals.stream().filter(Objects::nonNull).count();     return sum.divide(new BigDecimal(count), roundingMode); } 

Is there a way to do this without streaming twice (the second time to get the count)?

like image 579
Patrick Garner Avatar asked Aug 07 '15 15:08

Patrick Garner


People also ask

How do you know if two BigDecimals are equal?

math. BigDecimal. equals() method checks for equality of a BigDecimal value with the object passed. This method considers two BigDecimal objects equal if only if they are equal in value and scale.

How can I compare two BigDecimal values?

compareTo(BigDecimal val) compares the BigDecimal Object with the specified BigDecimal value. Two BigDecimal objects that are equal in value but have a different scale (like 2.0 and 2.00) are considered equal by this method.

How do you sum BigDecimal values?

math. BigDecimal. add(BigDecimal val) is used to calculate the Arithmetic sum of two BigDecimals. This method is used to find arithmetic addition of large numbers of range much greater than the range of largest data type double of Java without compromising with the precision of the result.


1 Answers

BigDecimal[] totalWithCount                 = bigDecimals.stream()                 .filter(bd -> bd != null)                 .map(bd -> new BigDecimal[]{bd, BigDecimal.ONE})                 .reduce((a, b) -> new BigDecimal[]{a[0].add(b[0]), a[1].add(BigDecimal.ONE)})                 .get(); BigDecimal mean = totalWithCount[0].divide(totalWithCount[1], roundingMode); 

Optional text description of the code for those that are find that to be helpful (Ignore if you find the code sufficiently self explanatory.):

  • The list of BigDecimals is converted to a stream.
  • null values are filtered out of the stream.
  • The stream of BigDecimals is mapped to as stream of two element arrays of BigDecimal where the first element is the element from the original stream and the second is the place holder with value one.
  • In the reduce the a of (a,b) value has the partial sum in the first element and the partial count in the second element. The first element of the b element contains each of the BigDecimal values to add to the sum. The second element of b is not used.
  • Reduce returns an optional that will be empty if the list was empty or contained only null values.
    • If the Optional is not empty, Optional.get() function will return a two element array of BigDecimal where the sum of the BigDecimals is in the first element and the count of the BigDecimals is in the second.
    • If the Optional is empty, NoSuchElementException will be thrown.
  • The mean is computed by dividing the sum by the count.
like image 120
WillShackleford Avatar answered Sep 30 '22 01:09

WillShackleford