Java 如何惰性地计算嵌套的flatMap
我试图从两个潜在无限的流中变出一个笛卡尔积,然后通过Java 如何惰性地计算嵌套的flatMap,java,java-stream,lazy-evaluation,cartesian-product,Java,Java Stream,Lazy Evaluation,Cartesian Product,我试图从两个潜在无限的流中变出一个笛卡尔积,然后通过limit()对其进行限制 到目前为止,这(大约)是我的策略: @测试 void flatMapIsLazy(){ “a”、“b”、“c”流 .flatMap(s->Stream.of(“x”、“y”) .flatMap(sd->IntStream.rangeClosed(0,整数.MAX_值) .mapToObj(sd::repeat))) .地图(s->s+“u”) .限额(20) .forEach(System.out::println)
limit()
对其进行限制
到目前为止,这(大约)是我的策略:
@测试
void flatMapIsLazy(){
“a”、“b”、“c”流
.flatMap(s->Stream.of(“x”、“y”)
.flatMap(sd->IntStream.rangeClosed(0,整数.MAX_值)
.mapToObj(sd::repeat)))
.地图(s->s+“u”)
.限额(20)
.forEach(System.out::println);
}
这不管用
显然,我的第二个流在第一次用于管道时就在现场进行最终评估。它不会产生一个懒惰的流,然后我可以按照自己的速度消费
我认为这段代码中的.forEach
应该归咎于ReferencePipeline#flatMap
:
@覆盖
公共无效接受(P_OUT u){
try(Stream正如您已经编写的,这已经被认为是一个bug。也许,它将在Java的未来版本中得到解决
但即使是现在也可能有一个解决方案。它不是很优雅,只有在外部流中的元素数量和限制足够小的情况下才可能可行。但它将在这些限制下工作
首先,让我稍微修改一下您的示例,将外部flatMap
转换为两个操作(一个map
和一个flatMap
带有标识,只执行展平操作):
我们可以很容易地看到,每个内部流不需要超过20个元素。因此,我们可以将每个流的元素数限制为这个数量。这将起作用(您应该使用变量或常量作为限制):
当然,这仍然会产生太多的中间结果,但在上述限制下,这可能不是一个大问题。除了流大小之外,您是否尝试过只运行“x”。重复(Integer.MAX\u值)
一次?在我的机器上,我得到一个OOM。也许这只是一个很糟糕的例子,但你不能期望它工作。除了上面的,.flatMap(s->second)
无法工作。您正在尝试重用流。这几乎肯定会给您带来一个非法状态异常。与原始查询更真实的代码版本可以是:stream.of(“a”、“b”、“c”).flatMap(s->stream.of(“x”、“y”).flatMap(sd->IntStream.rangeClosed(0,Integer.MAX_值)。mapToObj(sd::repeat)).map(s->s+“u”).limit(20).forEach(System.out::println);
,这会导致OOM。注意,它有嵌套的flatMap调用。@ernest_k是的,就是这样。我更改了问题代码!谢谢!:)我假设您会提供一些值(比如限制方法的整数)控制输出的大小。看到几个这样的值的预期输出会很有用。这很好!在我的实际测试代码中,它不起作用,因为我flatMap
和limit
在非常不同的位置和堆栈深度,因为内部和外部流是在不同的类中生成的。也许我可以使我不能在电话中来回走动,但有点达不到目的。无论如何,谢谢你的时间和回答。
a
ax
axx
axxx
axxxx
...
axxxxxxxxxxxxxxxxxxx
a
ax
axx
axxx
a
ay
ayy
ayyy
b
bx
bxx
bxxx
...
(up until 20 lines)
Stream.of("a", "b", "c")
.map(s -> Stream.of("x", "y")
.flatMap(sd -> IntStream.rangeClosed(0, Integer.MAX_VALUE)
.mapToObj(sd::repeat)))
.flatMap(s -> s)
.map(s -> s + "u")
.limit(20)
.forEach(System.out::println);
Stream.of("a", "b", "c")
.map(s -> Stream.of("x", "y")
.flatMap(sd -> IntStream.rangeClosed(0, Integer.MAX_VALUE)
.mapToObj(sd::repeat)))
.flatMap(s -> s.limit(20)) // limit each inner stream
.map(s -> s + "u")
.limit(20)
.forEach(System.out::println);