How to multiply values in a list using java 8 streams

后端 未结 4 677
长情又很酷
长情又很酷 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 list = new ArrayList();
    
        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 list = new ArrayList();
    
        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()

提交回复
热议问题