Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to multiply values in a list using java 8 streams

People also ask

What is reduce () in java8?

In Java 8, the Stream. reduce() combine elements of a stream and produces a single value. A simple sum operation using a for loop.

Does Java 8 support streams?

Java 8 offers the possibility to create streams out of three primitive types: int, long and double. As Stream<T> is a generic interface, and there is no way to use primitives as a type parameter with generics, three new special interfaces were created: IntStream, LongStream, DoubleStream.

Are Java 8 streams lazy?

The Java 8 Streams API is fully based on the 'process only on demand' strategy and hence supports laziness. In the Java 8 Streams API, the intermediate operations are lazy and their internal processing model is optimised to make it being capable of processing the large amount of data with high performance.


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.


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.


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()