【问题标题】:Iterating over two lists using Java 8 streams使用 Java 8 流迭代两个列表
【发布时间】:2015-05-20 22:05:24
【问题描述】:

如何在 Java 8 流中编写以下内容?

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

return total;

【问题讨论】:

    标签: lambda java-8 java-stream


    【解决方案1】:

    将嵌套的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();
    

    【讨论】:

      【解决方案2】:

      以下是在许多情况下可能更可取的替代解决方案:

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

      【讨论】:

      • 使用 flatMapToInt insdead 和 mapToInt 有什么区别?
      • mapToInt 将每个元素转换为另一个元素,而flatMapToInt 将单个元素转换为多个元素的序列。
      【解决方案3】:

      相当简单:将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();
      }
      

      【讨论】:

        【解决方案4】:

        对于同时走两个流的更一般的解决方案,有这个不是很好,但它有效的解决方案。

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

        【讨论】:

        • 这不是 OP 想要的。您正在同步迭代两个流,并且作者想要为 listA 的每个值迭代 listB 列表的所有值。您的版本是“点积”,而 OP 想要一个“交叉积”。
        • @TagirValeev - 确实如此 - 但此答案适用于问题标题使用 Java 8 流迭代两个列表
        猜你喜欢
        • 2015-06-11
        • 2015-02-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-03
        相关资源
        最近更新 更多