Iterating over two lists using Java 8 streams

How can I write the following in Java 8 streams?

int total = 0;
  for (ObjectA obja : rootObj.getListA()) {
    for (ObjectB objb : obja.getListB()) {
        total += objb.getCount() * obja.getCount();
    }
   }

return total;

      

+3


source to share


4 answers


The canonical solution to convert nested loops for

to is Stream

used via flatMap

:

return rootObj.getListA().stream()
.flatMapToInt(objA->objA.getListB().stream()
                                   .mapToInt(objB->objB.getCount() * objA.getCount()))
.sum();

      

This allows the operation to be performed for every inner iteration. However, in the special case of summation, you can simplify the operation, since it doesn't matter if you calculate (a+b+c+d)

or (a+b)+(c+d)

:



return rootObj.getListA().stream()
.mapToInt(objA->objA.getListB().stream()
                               .mapToInt(objB->objB.getCount() * objA.getCount()).sum())
.sum();

      

And when we remember elementary arithmetic, we must also remember what (a*x)+(b*x)

is equal (a+b)*x

, in other words, there is no need to multiply each element ListB

by a counter, objA

as we can also simply multiply the resulting sum by this count:

return rootObj.getListA().stream()
.mapToInt(objA->objA.getListB().stream().mapToInt(ObjectB::getCount).sum()*objA.getCount())
.sum();

      

+4


source


Here's an alternative solution that may be preferable in a number of cases:



int total = rootObj.getListA().stream()
    .flatMapToInt(objA -> objA.getListB()
         .stream().mapToInt(objB -> objB.getCount() * objA.getCount()))
    .sum();

      

+3


source


Pretty easy: compare ObjectA

with the sum of all yours ObjectB::getCount

multiplied by your own getCount()

, then just sum IntStream

:

int total = rootObj.getListA().stream()
    .mapToInt(obja -> obja.getCount() * obja.getListB().stream().mapToInt(ObjectB::getCount).sum())
    .sum();

      

To improve readability, you can introduce a private helper method:

int total = rootObj.getListA().stream()
    .mapToInt(this::calculate)
    .sum();

      

with a helper method:

private int calculate(ObjectA obja) {
    return obja.getListB().stream()
            .mapToInt(ObjectB::getCount)
            .sum() * obja.getCount();
}

      

+2


source


And for a more general solution of walking two threads at once, this is not very pleasant, but it works.

public static <A, B, C> Stream<C> zip(
        Stream<A> a,
        Stream<B> b,
        BiFunction<A, B, C> op) {
    Iterator<A> i1 = a.iterator();
    Iterator<B> i2 = b.iterator();
    Iterable<C> i = () -> new Iterator<C>() {
        @Override
        public boolean hasNext() {
            return i1.hasNext() && i2.hasNext();
        }

        @Override
        public C next() {
            return op.apply(i1.next(), i2.next());
        }

    };
    // Not certain whether we can do this in parallel - probably not.
    return StreamSupport.stream(i.spliterator(), false);
}

      

+2


source







All Articles