How to multiply values in a list using java 8 streams
Try reduce of streams, it should help.
Like:
listOfIntegers.stream().reduce(1, (a, b) -> a * b)
This link provides more information on how to use reduce.
I contribute to @Misha answer because of I have found an example where we would want use BigInteger rather than primitive datatypes for multiplication.
I was solving this kata: Numbers with this digit inside, where we are given x: an int and d: a digit. We need to find the numbers from 1 to x which contain d, and return its count, sum and multiplication as a long array.
First I tried the following code:
import java.util.*;
public class Kata
{
public static long[] NumbersWithDigitInside(long x, long d)
{
if(d > x) return new long[3];
List<Integer> list = new ArrayList<Integer>();
for(int i = 1; i <= x; i++){
String current = String.valueOf(i);
if(current.contains(String.valueOf(d))){
list.add(i);
}
}
return new long[]{list.size(),
list.stream().mapToInt(Integer::intValue).sum(),
list.stream().reduce(1, (a,b) -> a*b)};
}
}
When we execute the following tests:
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
import org.junit.runners.JUnit4;
public class SolutionTest {
@Test
public void BasicTests() {
assertArrayEquals(new long[] { 0, 0, 0 }, Kata.NumbersWithDigitInside(5, 6));
assertArrayEquals(new long[] { 1, 6, 6 }, Kata.NumbersWithDigitInside(7, 6));
assertArrayEquals(new long[] { 3, 22, 110 }, Kata.NumbersWithDigitInside(11, 1));
assertArrayEquals(new long[] { 2, 30, 200 }, Kata.NumbersWithDigitInside(20, 0));
assertArrayEquals(new long[] { 9, 286, 5955146588160L }, Kata.NumbersWithDigitInside(44, 4));
}
}
It outputs:
arrays first differed at element [2]; expected:<5955146588160> but was:<-1973051392>
Because of it fails when attempting the last test case:
assertArrayEquals(new long[] { 9, 286, 5955146588160L }, Kata.NumbersWithDigitInside(44, 4));
So to check if it was due to an overflown I replaced:
list.stream().reduce(1, (a,b) -> a*b)};
With:
list.stream().mapToInt(num->num).reduce(1, Math::multiplyExact)};
So then, it outputs:
java.lang.ArithmeticException: integer overflow
Finally I used BigInteger as follows:
list.stream().map(BigInteger::valueOf).reduce(BigInteger.ONE, BigInteger::multiply).longValue()}
Being the complete code:
import java.util.*;
import java.math.BigInteger;
public class Kata
{
public static long[] NumbersWithDigitInside(long x, long d)
{
List<Integer> list = new ArrayList<Integer>();
for(int i = 1; i <= x; i++){
String current = String.valueOf(i);
if(current.contains(String.valueOf(d))){
list.add(i);
}
}
if(list.size() == 0) return new long[3];
return new long[]{list.size(),
list.stream().mapToInt(Integer::intValue).sum(),
list.stream().map(BigInteger::valueOf).reduce(BigInteger.ONE, BigInteger::multiply).longValue()};
}
}
➡️ For further information:
BigInteger class longValue()
One thing to keep in mind when multiplying an unknown number of ints
is the possibility of overflows. Rather than (a,b) -> a*b
, it is safer to use Math::multiplyExact
, which will throw an exception on overflow:
listOfIntegers.stream().mapToInt(x->x).reduce(1, Math::multiplyExact);
Alternatively, you can accommodate large results by reducing on BigInteger
:
listOfIntegers.stream()
.map(BigInteger::valueOf)
.reduce(BigInteger.ONE, BigInteger::multiply);
Reduction with an identity will return 1
or BigInteger.ONE
if the list is empty, which may not be what you want. If you wish to handle the case of an empty list, remove the first argument to reduce
and then deal with the resulting Optional
.