6

I have two ArrayLists, named A and B, of equal size containing some numbers. Now I want to calculate something like this:

int sum = 0;
for(int i=0; i<A.size() && i<B.size(); i++) {
  sum += A.get(i)*B.get(i);
}

How can I achieve what I am doing above, calculating the sum, by using Java 8 features (streams, lambda expressions, etc) without using any extra user-defined methods?

Jeffrey Bosboom
  • 13,313
  • 16
  • 79
  • 92
Mubin
  • 4,192
  • 3
  • 26
  • 45
  • 2
    You might get a better answer if you explain why the loop is not acceptable to you, but you can use the rather literal stream translation of that loop with `IntStream.range(0, A.size()).map(i -> A.get(i) * B.get(i)).sum()`. – Jeffrey Bosboom Jun 28 '15 at 02:38
  • 1
    Just for good programming measure: I don't see any proof that A and B are the same size. – Mike 'Pomax' Kamermans Jun 28 '15 at 02:39
  • @JeffreyBosboom I have no problems with using loops. I asked it because I am learning Java 8 features, should have included that in the post. – Mubin Jun 28 '15 at 02:55
  • 1
    possible duplicate of [Iterate two Java-8-Streams together](http://stackoverflow.com/questions/24059837/iterate-two-java-8-streams-together) – Tagir Valeev Jun 28 '15 at 03:48

2 Answers2

15
int sum = 
    IntStream.range(0, min(a.size(), b.size())
             .map(i -> a.get(i) * b.get(i))
             .sum();
Jeffrey Bosboom
  • 13,313
  • 16
  • 79
  • 92
Brian Goetz
  • 90,105
  • 23
  • 150
  • 161
  • although of course given the claim that they're already same-sized, no need for that `min()` call – Mike 'Pomax' Kamermans Jun 28 '15 at 02:38
  • 1
    I think it's multiplication, not addition. `a.get(i) + b.get(i)` should be `a.get(i) * b.get(i)`? – almightyGOSU Jun 28 '15 at 02:41
  • 1
    That's a neat solution. I hadn't considered emitting the range of indices as a Stream. – Alain O'Dea Jun 28 '15 at 02:47
  • How can this be adapted for `ArrayList` of `Double`s? I don't see a `range` method on `DoubleStream` – Mubin Jun 28 '15 at 02:48
  • 2
    @Mubin - It will work "as is" for any collections that support positional access. – PM 77-1 Jun 28 '15 at 02:51
  • 3
    @Mubin the IntStream.range generates a Stream of indices, not a Stream of values to be multiplied. All Lists are indexable by integer indexes regardless of contained types. – Alain O'Dea Jun 28 '15 at 02:51
  • 2
    For using `ArrayLists` with `Double` values, I had to use `mapToDouble` instead of `map`, otherwise it was giving `Type mismatch: cannot convert from double to int` – Mubin Jun 28 '15 at 03:05
  • @Mubin **IntStream.map** expects an **IntUnaryOperator** and returns an **IntStream**. So **mapToDouble** is required so that you can provide a **IntToDoubleFunction** lamdba to get a **DoubleStream** for **sum()**. https://docs.oracle.com/javase/8/docs/api/java/util/stream/IntStream.html#map-java.util.function.IntUnaryOperator- – Alain O'Dea Jun 30 '15 at 23:45
  • this is a nice hack, but it's not really streaming the lists, you are simply creating an IntStream of Indexes. The main issue would again be performance. – moldovean Nov 30 '16 at 15:33
0
int sum = IntStream
            .range(0, min(a.size(), b.size())
            .map(i -> 
              (a.get(i).getValue() == null || b.get(i).getValue() == null) ? 0 :
                       (a.get(i).getValue() * b.get(i).getValue()))
            .sum();

Neat Solution!
I had a similar requirement, and I added ternary operator null check. (In my scenario, my lists where of generic type and calculation was done on values within the object which could be null)

Daenerys
  • 462
  • 4
  • 8