How to multiply values in a list using java 8 streams

后端 未结 4 636
长情又很酷
长情又很酷 2020-12-29 18:06

Is there a sum() equivalent method in stream which can perform multiplication of values given in a stream?

I\'ve a list of Integers like this :

List         


        
相关标签:
4条回答
  • 2020-12-29 18:51

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

    0 讨论(0)
  • 2020-12-29 18:56

    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.

    0 讨论(0)
  • 2020-12-29 18:59

    And here is an example for decimal products:

    // Some list
    List<Double> values;
    ...
    double product = values.stream()
        .reduce(1.0, (acc, value) -> acc * value)
        .doubleValue();
    
    0 讨论(0)
  • 2020-12-29 19:10

    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.

    0 讨论(0)
提交回复
热议问题