使用Java 8流迭代两个列表

4
我如何在Java 8 Streams中编写以下内容?
int total = 0;
  for (ObjectA obja : rootObj.getListA()) {
    for (ObjectB objb : obja.getListB()) {
        total += objb.getCount() * obja.getCount();
    }
   }

return total;
4个回答

5

将嵌套的for循环转换为Stream API的规范解决方案是通过使用flatMap函数:

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

这使您能够为每个内部迭代执行操作。然而,在求和的特殊情况下,您可以简化操作,因为计算(a+b+c+d)(a+b)+(c+d)没有区别:

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

当我们回忆起基本算术时,应该记住(a*x)+(b*x)等于(a+b)*x,换句话说,没有必要将ListB的每个项都乘以objA的计数,因为我们也可以将得到的总和乘以那个计数:

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

3

这里有一个备选方案,在许多情况下可能更可取:

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

1
使用flatMapToInt和mapToInt有什么区别? - jliakos
2
mapToInt 将每个元素转换为另一个元素,而 flatMapToInt 则将单个元素转换为多个元素的序列。 - Tagir Valeev

2
相对容易的任务:将ObjectA映射到其所有ObjectB::getCount乘以自身getCount()之和,然后简单地求和IntStream
int total = rootObj.getListA().stream()
    .mapToInt(obja -> obja.getCount() * obja.getListB().stream().mapToInt(ObjectB::getCount).sum())
    .sum();

为了提高可读性,您可以引入一个私有的帮助方法:
int total = rootObj.getListA().stream()
    .mapToInt(this::calculate)
    .sum();

使用帮助方法:

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

2

对于同时处理两个流的更一般解决方案,有这个不太好看但可行的解决方案。

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
这不是 OP 想要的。你正在同步迭代两个流,而作者想要迭代 listA 的每个值的所有 listB 列表值。你的版本是“点积”,而 OP 想要一个“叉积”。 - Tagir Valeev
@TagirValeev - 这是真的 - 但这个答案适用于问题标题使用Java 8流迭代两个列表 - OldCurmudgeon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接