lambda - Why filter() after flatMap() is "not completely" lazy in Java streams? -
i have following sample code:
system.out.println( "result: " + stream.of(1, 2, 3) .filter(i -> { system.out.println(i); return true; }) .findfirst() .get() ); system.out.println("-----------"); system.out.println( "result: " + stream.of(1, 2, 3) .flatmap(i -> stream.of(i - 1, i, + 1)) .flatmap(i -> stream.of(i - 1, i, + 1)) .filter(i -> { system.out.println(i); return true; }) .findfirst() .get() ); the output follows:
1 result: 1 ----------- -1 0 1 0 1 2 1 2 3 result: -1 from here see in first case stream behaves lazily - use findfirst() once have first element our filtering lambda not invoked. however, in second case uses flatmaps see despite first element fulfils filter condition found (it's first element lambda returns true) further contents of stream still being fed through filtering function.
i trying understand why behaves rather giving after first element calculated in first case. helpful information appreciated.
when looking implementation (referencepipeline.java) see method [link]
@override final void foreachwithcancel(spliterator<p_out> spliterator, sink<p_out> sink) { { } while (!sink.cancellationrequested() && spliterator.tryadvance(sink)); } which invoke findfirst operation. special thing take care sink.cancellationrequested() allows end loop on first match. compare [link]
@override public final <r> stream<r> flatmap(function<? super p_out, ? extends stream<? extends r>> mapper) { objects.requirenonnull(mapper); // can better this, polling cancellationrequested when stream infinite return new statelessop<p_out, r>(this, streamshape.reference, streamopflag.not_sorted | streamopflag.not_distinct | streamopflag.not_sized) { @override sink<p_out> opwrapsink(int flags, sink<r> sink) { return new sink.chainedreference<p_out, r>(sink) { @override public void begin(long size) { downstream.begin(-1); } @override public void accept(p_out u) { try (stream<? extends r> result = mapper.apply(u)) { // can better too; optimize depth=0 case , grab spliterator , foreach if (result != null) result.sequential().foreach(downstream); } } }; } }; } the method advancing 1 item ends calling foreach on sub-stream without possibility earlier termination , comment @ beginning of flatmap method tells absent feature.
since more optimization thing implies code breaks when sub-stream infinite, hope developers prove “can better this”…
to illustrate implications, while stream.iterate(0, i->i+1).findfirst() works expected, stream.of("").flatmap(x->stream.iterate(0, i->i+1)).findfirst() end in infinite loop.
regarding specification, of can found in the
chapter “stream operations , pipelines” of package specification:
…
intermediate operations return new stream. lazy;
…
… laziness allows avoiding examining data when not necessary; operations such "find first string longer 1000 characters", necessary examine enough strings find 1 has desired characteristics without examining of strings available source. (this behavior becomes more important when input stream infinite , not merely large.)
…
further, operations deemed short-circuiting operations. intermediate operation short-circuiting if, when presented infinite input, may produce finite stream result. terminal operation short-circuiting if, when presented infinite input, may terminate in finite time. having short-circuiting operation in pipeline necessary, not sufficient, condition processing of infinite stream terminate in finite time.
it’s clear short-circuiting operation doesn’t guaranty finite time termination, e.g. when filter doesn’t match item processing can’t complete, implementation doesn’t support termination in finite time ignoring short-circuiting nature of operation far off specification.
Comments
Post a Comment