I'm trying to conjure up a cartesian product from two potentially infinite streams that I then limit via limit()
.
This has been (approximately) my strategy so far:
@Test
void flatMapIsLazy() {
Stream.of("a", "b", "c")
.flatMap(s -> Stream.of("x", "y")
.flatMap(sd -> IntStream.rangeClosed(0, Integer.MAX_VALUE)
.mapToObj(sd::repeat)))
.map(s -> s + "u")
.limit(20)
.forEach(System.out::println);
}
This doesn't work.
Apparently, my second stream gets terminally-evaluated on the spot the first time it is used on the pipeline. It doesn't produce a lazy stream that I can then consume at my own pace.
I think the .forEach
in this piece of code from ReferencePipeline#flatMap
is to blame:
@Override
public void accept(P_OUT u) {
try (Stream<? extends R> result = mapper.apply(u)) {
if (result != null) {
if (!cancellationRequestedCalled) {
result.sequential().forEach(downstream);
}
else {
var s = result.sequential().spliterator();
do { } while (!downstream.cancellationRequested() && s.tryAdvance(downstream));
}
}
}
}
I expected the above code to return 20 elements looking like:
a
ax
axx
axxx
axxxx
...
axxxxxxxxxxxxxxxxxxx
But instead it crashes with an OutOfMemoryError
, since the very long Stream
of the nested flatMap
is evaluated eagerly (??) and fills up my memory with unnecessary copies of the repeated strings. If instead of Integer.MAX_VALUE
, a value of 3 was provided, keeping the same limit at 20, expected output would instead be:
a
ax
axx
axxx
a
ay
ayy
ayyy
b
bx
bxx
bxxx
...
(up until 20 lines)
Edit: At this point I have just rolled my own implementation with lazy iterators. Still, I think there should be a way to do this with pure Streams.
Edit 2: This has been admitted as a bug ticket in Java https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8267758%20
"x".repeat(Integer.MAX_VALUE)
once? On my machine, I get an OOM. Maybe this is just a bad example you have here, but you can't expect it to work. – Irreconcilable.flatMap(s -> second)
can't work. You're trying to reuse a stream. That will almost certainly give you an illegalstateexception. – IrreconcilableString#repeat()
withInteger.MAX_VALUE
as an argument. The repeated strings would generate one by one as needed. About Stream reuse, what stream instance do you see being reused? Oh, I get it, yeah. I'll update that one, thanks. – SandraStream.of("a", "b", "c").flatMap(s -> Stream.of("x", "y").flatMap(sd -> IntStream.rangeClosed(0, Integer.MAX_VALUE).mapToObj(sd::repeat))).map(s -> s + "u").limit(20).forEach(System.out::println);
, which causes OOM. Note it has nested flatMap calls. – IrreconcilableIterator
implementation now and it's doing wonders but I'd rather be able to use theStream
API instead. – Sandra