【问题标题】:Composing streams with flatmap in Java 8在 Java 8 中使用 flatmap 组合流
【发布时间】:2015-03-04 07:48:06
【问题描述】:

假设我有以下课程:

class A {
   int i, j, k;

   public A(int i, int j, int k) {
     this.i = i; this.j = j; this.k = k;
   }
}

其中ijk 具有已知范围:r_ir_jr_k。现在我想生成 此范围内所有可能的A 实例。我可以想出类似的东西:

Stream.iterate(0, n -> ++n).limit(r_i)
.flatMap(i -> Stream.iterate(0, n -> ++n).limit(r_j)
.flatMap(j -> Stream.iterate(0, n -> ++n).limit(r_k)
.map(k -> new A(i, j, k)))).collect(Collectors.toList())

首先,它太冗长了。有没有办法缩短它?特别是我找不到rangeStream。其次,编译器无法确定返回类型的类型。它认为它是 List<Object> 而不是预期的 List<A>。我该如何解决?

【问题讨论】:

  • 您可能想先查看IntStream.range
  • @JonSkeet 我试过了,但还有其他问题。那里的 flatMap 不允许任意返回类型,只有整数。有一个尴尬的mapToObj方法,但是我没有找到flatmapToObj。
  • 这绝对可以用IntStream.rangemapToObj结尾。您确定您的编译器会抱怨还是只是您的 IDE?我刚刚在 IntelliJ IDEA 14 中复制了您的代码,它没有显示任何警告或错误或任何类型。
  • @Ali 我确实做到了,而且效果很好。我正在使用 Java 8 Build 20 和 IntelliJ 14。
  • @Ali 是的,我也可以让它工作。这不是类型推断的问题。 Java 编译器做得很好,但是许多 IDE 在这方面充满了错误。 Intellij 14 刚刚发布时在这方面存在很多问题,并且随着每个新版本的发布,它们都得到了很大的改进。我想,开发人员一直在研究他们自己的 IDE 类型推断算法,试图尽可能地模拟编译器的功能。但是很可能这里和那里仍然存在错误。随着时间的推移,这会变得更好,但你应该最终相信你的编译器,而不是 IDE。

标签: java java-8 java-stream flatmap


【解决方案1】:

如果可以有可变变量,你可以像这样得到 A 类的列表....

List<A> newCollect = new ArrayList<>();
IntStream.range(0, r_i).forEach(
    i -> IntStream.range(0, r_j).forEach(
        j -> IntStream.range(0, r_k).forEach(
            k -> newCollect.add(new A(i, j, k))
        )
    )
);

或者你可以制作 List of List of A List of A,然后 flatMap 像这样两次...

List<A> newCollect2 = IntStream.range(0, r_i).mapToObj(
    i -> IntStream.range(0, r_j).mapToObj(
        j -> IntStream.range(0, r_k).mapToObj(
            k -> new A(i, j, k)
        ).collect(Collectors.toList())
    ).collect(Collectors.toList())
)
.flatMap(a -> a.stream())
.flatMap(a -> a.stream())
.collect(Collectors.toList());

【讨论】:

    【解决方案2】:

    使用range 的一种方法是在之后立即执行装箱转换:

    List<A> list=IntStream.range(0, r_i).boxed()
      .flatMap(i -> IntStream.range(0, r_j).boxed()
        .flatMap(j -> IntStream.range(0, r_k)
          .mapToObj(k -> new A(i, j, k)))).collect(Collectors.toList());
    

    这不是最漂亮的代码,但IntStream.range(0, max).boxed() 仍然比Stream.iterate(0, n -&gt; n+1).limit(max) 好......


    一种替代方法是使用真正的展平操作而不是嵌套操作:

    List<A> list=IntStream.range(0, r_i).boxed()
      .flatMap(i  -> IntStream.range(0, r_j).mapToObj(j -> new int[]{i,j}))
      .flatMap(ij -> IntStream.range(0, r_k).mapToObj(k -> new A(ij[0], ij[1], k)))
      .collect(Collectors.toList());
    

    我看到的主要缺点是缺少IntPairTuple&lt;int,int&gt; 类型。因此它使用数组作为解决方法。

    【讨论】:

      猜你喜欢
      • 2018-06-02
      • 2022-09-26
      • 1970-01-01
      • 2015-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-26
      相关资源
      最近更新 更多