Viktor and I separately suggested to use the 3-arg iterate() method;
it will always be correct and robust for what the snippet of yours is
trying to achieve. If you look into its documentation, you will see
that the 3-arg iterate() method is specified to behave like a
for-loop, and the for-loop's order of operations is well-defined.

As for the guarantee __for your snippet__, the closest I can see is
this (below emphasis is mine):

    Streams are lazy; computation on the source data is only performed
when the terminal operation is initiated, and __source elements are
consumed only as needed__.

In my mind, this prohibits any sort of buffering or prefetch "just in
case". Do we need to source more elements beyond the ones accepted by
takeWhile()? No, we don't. Then we don't need them.

Again, I'm not an expert. Viktor is. So, listen to him more than you
listen to me.

On Tue, Mar 3, 2026 at 7:34 PM Jige Yu <[email protected]> wrote:
>
> Hi Pavel,
>
> I think you've raised a critical point.
>
> My impression of the Stream specification is that it defines the order of 
> inputs and outputs but refrains from strictly defining the order of execution.
>
> Specifically, the stream guarantees it will call iterate() to produce 
> elements in encounter order (e.g., 1 and 2) and send them to takeWhile() in 
> the same order. However, does it actually promise a sequence of iterate() -> 
> takeWhile() -> iterate() -> takeWhile()? Or could it execute as iterate() -> 
> iterate() -> takeWhile() -> takeWhile()?
>
> I believe both orders conform to the spec, but the latter would result in a 
> NullPointerException. Does that distinction make sense?
>
> Best,
>
> Jige Yu
>
> On Sun, Mar 1, 2026 at 2:56 PM Pavel Rappo <[email protected]> wrote:
>>
>> I'm neither an expert in streams nor an everyday user. So take this
>> with a grain of salt.
>>
>> What you seem to want has nothing to do with happens-before.
>> Happens-before is about memory model. What you want is a guarantee
>> that iterate() will not be called after it has produced null, because
>> doing so would mean your idiom is buggy.
>>
>> I think you have that guarantee. iterate() produces a sequential
>> ordered stream. takeWhile() is an intermediate operation, and it does
>> not change whether the stream is parallel or sequential. Unless you
>> insert parallel() between these two calls, a terminal operation will
>> not cause iterate() to produce more than one null element because
>> streams are lazy. Put differently, you should never see a
>> NullPointerException thrown from your snippet. That's my
>> understanding.
>>
>> FWIW, there's a fused overload of iterate() that additionally takes
>> the termination condition that your snippet passes to takeWhile(). It
>> will behave correctly and similarly to your snippet even if the stream
>> is later parallelized.
>>
>> On Sun, Mar 1, 2026 at 5:29 AM Jige Yu <[email protected]> wrote:
>> >
>> > Hi @core-libs-dev,
>> >
>> > I am looking to validate the following idiom:
>> >
>> > Stream.iterate(seed, e -> e.nextOrNull())
>> >     .takeWhile(Objects::nonNull);
>> >
>> > The intent is for the stream to call nextOrNull() repeatedly until it 
>> > returns null. However, I am concerned about where the Stream specification 
>> > guarantees the correctness of this approach regarding happens-before 
>> > relationships.
>> >
>> > The iterate() Javadoc defines happens-before for the function passed to 
>> > it, stating that the action of applying f for one element happens-before 
>> > the action of applying it for subsequent elements. However, it seems 
>> > silent on the happens-before relationship with downstream operations like 
>> > takeWhile().
>> >
>> > My concern stems from the general discouragement of side effects in stream 
>> > operations. For example, relying on side effects between subsequent map() 
>> > calls is considered brittle because a stream might invoke the first map() 
>> > on multiple elements before the second map() processes the first element.
>> >
>> > If this theory holds, is there anything theoretically preventing iterate() 
>> > from generating multiple elements before takeWhile() evaluates the first 
>> > one? I may be overthinking this, but I would appreciate your insights into 
>> > why side effects are discouraged even in ordered, sequential streams and 
>> > whether this specific idiom is safe.
>> >
>> > Appreciate your help!
>> >
>> > Best regards,
>> > Jige Yu

Reply via email to