Iterating over two lists using Java 8 streams

为君一笑 提交于 2019-12-06 10:22:00

问题


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;

回答1:


The canonical solution for converting nested for loops to Stream API usage is via flatMap:

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

This allows you to perform an operation for each inner iteration. However, in the special case of summing you may simplify the operation as it doesn’t matter whether you compute (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 are at remembering elementary arithmetics we should also recall that (a*x)+(b*x) is equal to (a+b)*x, in other words, there is no need to multiply every item of ListB with the count of objA as we can also just multiple the resulting sum with that count:

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



回答2:


Here's an alternative solution which might 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:


Fairly easy : map ObjectA to the sum of all its ObjectB::getCount multiplied by its own getCount(), then simply sum the 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 helper method :

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



回答4:


And for the more general solution of walking two streams at once there's this not-very-nice but it works solution.

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);
}


来源:https://stackoverflow.com/questions/30361197/iterating-over-two-lists-using-java-8-streams

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!