Viktor, can you be more specific about how you see this statement apply to
the question of generate()'s unordered-ness?

On Fri, Mar 6, 2026 at 1:07 AM Viktor Klang <[email protected]> wrote:

> From that documentation:
>
> *«For sequential streams, the presence or absence of an encounter order
> does not affect performance, only determinism. If a stream is ordered,
> repeated execution of identical stream pipelines on an identical source
> will produce an identical result; if it is not ordered, repeated execution
> might produce different results.»*
> On 2026-03-06 04:00, Jige Yu wrote:
>
>
>
> On Thu, Mar 5, 2026 at 3:13 PM Viktor Klang <[email protected]>
> wrote:
>
>> >And if generate() is unordered, is it by-spec safe to depend on the
>> elements being delivered in the order they are generated, at all?
>>
>> Encounter order for unordered streams is described here:
>> https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/stream/package-summary.html#Ordering
>>
>
> Yeah. Specifically this statement:
>
> > if the source of a stream is a List containing [1, 2, 3], then the
> result of executing map(x -> x*2) must be [2, 4, 6]. However, if the
> source has no defined encounter order, then any permutation of the values [2,
> 4, 6] would be a valid result.
>
> generate()'s source isn't a List, and it's specified as "an infinite
> sequential unordered stream", which to me reads as "it has no defined
> encounter order".
>
> And if that reading is correct, then any permutation of the generated
> results would be a valid result?
>
> On 2026-03-06 00:04, Jige Yu wrote:
>>
>> From what I can see, many of these supplier lambdas do return null
>> idempotently, such as generate(queue::poll).
>>
>> But with enough usage, I suspect we'll run into scenarios that may trip
>> if called again after returning null.
>>
>> And if generate() is unordered, is it by-spec safe to depend on the
>> elements being delivered in the order they are generated, at all?
>>
>> On Thu, Mar 5, 2026 at 1:33 AM Viktor Klang <[email protected]>
>> wrote:
>>
>>> Is the supplier's get()-method allowed to be invoked *after* it has
>>> previously returned *null?*
>>> On 2026-03-05 06:54, Jige Yu wrote:
>>>
>>> Makes sense.
>>>
>>> What do you guys think of the idiom of
>>> generate(supplierThatEventuallyReturnsNull) + takeWhile() ? Should it be
>>> avoided?
>>>
>>> On Wed, Mar 4, 2026 at 6:59 AM Viktor Klang <[email protected]>
>>> wrote:
>>>
>>>> >In our codebase, I see some developers using iterate() + takeWhile()
>>>> and others using generate() + takeWhile(). I am debating whether to raise a
>>>> concern about this pattern. Most likely, people won't insert intermediary
>>>> operations between them, and I worry I might be overthinking it.
>>>>
>>>> In this specific case I'd argue that it's more correct (and more
>>>> performant, and less code) to just use the 3-arg iterate.
>>>>
>>>> >or should I reconsider my warnings about side effects being rearranged
>>>> in sequential streams?
>>>>
>>>> Personally I prefer my Streams correct regardless of underlying
>>>> implementation and regardless of whether the stream isParallel() or not.
>>>> On 2026-03-03 20:29, Jige Yu wrote:
>>>>
>>>> Hi Viktor,
>>>>
>>>> Thanks for the explanation!
>>>>
>>>> I also experimented with adding parallel() in the middle, and it indeed
>>>> threw a NullPointerException even without distinct().
>>>>
>>>> In our codebase, I see some developers using iterate() + takeWhile()
>>>> and others using generate() + takeWhile(). I am debating whether to raise a
>>>> concern about this pattern. Most likely, people won't insert intermediary
>>>> operations between them, and I worry I might be overthinking it.
>>>>
>>>> However, generate(supplierThatMayReturnNull).takeWhile() seems even
>>>> more precarious. Since generate() is documented as unordered, could it
>>>> potentially return elements out of encounter order, such as swapping a
>>>> later null with an earlier non-null return?
>>>>
>>>> This brings me back to the rationale I’ve used to discourage side
>>>> effects in map() and filter(). In a sequential stream, I’ve argued that
>>>> relying on side effects from an earlier map() to be visible in a subsequent
>>>> map() is unsafe because the stream is theoretically free to process
>>>> multiple elements through the first map() before starting the second.
>>>>
>>>> Is that view too pedantic? If we can safely assume iterate() +
>>>> takeWhile() is stable in non-parallel streams, should the same logic apply
>>>> to subsequent map() calls with side effects (style issues aside)?
>>>>
>>>> I’m trying to find a consistent theory. Should I advise my colleagues
>>>> that iterate() + takeWhile() and generate() + takeWhile() are unsafe, or
>>>> should I reconsider my warnings about side effects being rearranged in
>>>> sequential streams?
>>>>
>>>> I hope that clarifies the root of my confusion.
>>>>
>>>> Best,
>>>> Jige Yu
>>>>
>>>> On Mon, Mar 2, 2026 at 6:08 AM Viktor Klang <[email protected]>
>>>> wrote:
>>>>
>>>>> Hi Jige,
>>>>>
>>>>> I think I understand what you mean. In this case you're trying to
>>>>> prevent a `null`-return from `nextOrNull()` to be fed into the next
>>>>> iteration and thus throwing a NullPointerException.
>>>>>
>>>>> Now the answer is going to be a bit nuanced than you might want to
>>>>> hear, but in the spirit of providing clarity, the code which you provided
>>>>> will "work" under the assumption that there is no "buffer" in between
>>>>> iterate(…) and takeWhile(…).
>>>>>
>>>>> TL;DR: use Stream.iterate(seed, e -> e != null, e -> e.nextOrNull())
>>>>> Long version:
>>>>> Imagine we have the following:
>>>>> ```java
>>>>> record E(E e) {}
>>>>> Stream.iterate(new E(new E(new E(null))), e -> e.e())
>>>>> .< /span>takeWhile(Objects::nonNull)
>>>>> .forEach(IO::println)
>>>>> ```
>>>>> We get:
>>>>> ```java
>>>>> E[e=E[e=E[e=null]]]
>>>>> E[e=E[e=null]]
>>>>> E[e=null]
>>>>> ```
>>>>> However, if we do:
>>>>> ```java
>>>>> Stream.iterate(new E(new E(new E(null))), e -> e.e())< /span>
>>>>> .gather(
>>>>> Gatherer.<E,ArrayList<E>,E>ofSequential(
>>>>> ArrayList::new,
>>>>> (l, e, _) -> l.add(e),
>>>>> (l, d) -> l.forEach(d::push)
>>>>> )
>>>>> )
>>>>> .takeWhile(Objects::nonNull)
>>>>> .forEach(IO::println)
>>>>> ```
>>>>> We get:
>>>>> ```java
>>>>> Exception java.lang.NullPointerException: Cannot invoke
>>>>> "REPL.$JShell$16$E.e()" because "<parameter1>" is null
>>>>> at lambda$do_it$$0 (#5:1)
>>>>> at Stream$1.tryAdvance (Stream.java:1515)
>>>>> at ReferencePipeline.forEachWithCancel (ReferencePipeline.java:147)
>>>>> at AbstractPipeline.copyIntoWithCancel (AbstractPipeline.java:588)
>>>>> at AbstractPipeline.copyInto (AbstractPipeline.java:574)
>>>>> at AbstractPipeline.wrapAndCopyInto (AbstractPipeline.java:560)
>>>>> at ForEachOps$ForEachOp.evaluateSequential (ForEachOps.java:153)
>>>>> at ForEachOps$ForEachOp$OfRef.evaluateSequential (ForEachOps.java:176)
>>>>> at AbstractPipeline.evaluate (AbstractPipeline.java:265)
>>>>> at ReferencePipeline.forEach (ReferencePipeline.java:632)
>>>>> at (#5:9)
>>>>> ```
>>>>> But if we introduce something like `distinct()` in between, it will
>>>>> "work" under sequential processing,
>>>>> but under parallel processing it might not, as the distinct operation
>>>>> will have to buffer *separately* from takeWhile:
>>>>> ```java
>>>>> Stream.iterate(new E(new E(new E(null))), e -> e.e())< /span>
>>>>> .distinct()
>>>>> .takeWhile(Objects::nonNull)
>>>>> .forEach(IO::println)
>>>>> ```
>>>>> ```java
>>>>> E[e=E[e=E[e=null]]]
>>>>> E[e=E[e=null]]
>>>>> E[e=null]
>>>>> ```
>>>>> Parallel:
>>>>> ```java
>>>>> Stream.iterate(new E(new E(new E(null))), e -> e.e())< /span>
>>>>> .parallel()
>>>>> .distinct()
>>>>> .takeWhile(Objects::nonNull)
>>>>> .forEach(IO::println)
>>>>> ```
>>>>> ```java
>>>>> Exception java.lang.NullPointerException: Cannot invoke
>>>>> "REPL.$JShell$16$E.e()" because "<parameter1>" is null
>>>>> at lambda$do_it$$0 (#7:1)
>>>>> at Stream$1.tryAdvance (Stream.java:1515)
>>>>> at Spliterators$AbstractSpliterator.trySplit (Spliterators.java:1447)
>>>>> at AbstractTask.compute (AbstractTask.java:308)
>>>>> at CountedCompleter.exec (CountedCompleter.java:759)
>>>>> at ForkJoinTask.doExec (ForkJoinTask.java:511)
>>>>> at ForkJoinTask.invoke (ForkJoinTask.java:683)
>>>>> at ReduceOps$ReduceOp.evaluateParallel (ReduceOps.java:927)
>>>>> at DistinctOps$1.reduce (DistinctOps.java:64)
>>>>> at DistinctOps$1.opEvaluateParallelLazy (DistinctOps.java:110)
>>>>> at AbstractPipeline.sourceSpliterator (AbstractPipeline.java:495)
>>>>> at AbstractPipeline.evaluate (AbstractPipeline.java:264)
>>>>> at ReferencePipeline.forEach (ReferencePipeline.java:632)
>>>>> at (#7:4)
>>>>> ```
>>>>>
>>>>> On 2026-03-01 06:29, Jige Yu 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
>>>>>
>>>>> --
>>>>> Cheers,
>>>>> √
>>>>>
>>>>>
>>>>> Viktor Klang
>>>>> Software Architect, Java Platform Group
>>>>> Oracle
>>>>>
>>>>> --
>>>> Cheers,
>>>> √
>>>>
>>>>
>>>> Viktor Klang
>>>> Software Architect, Java Platform Group
>>>> Oracle
>>>>
>>>> --
>>> Cheers,
>>> √
>>>
>>>
>>> Viktor Klang
>>> Software Architect, Java Platform Group
>>> Oracle
>>>
>>> --
>> Cheers,
>> √
>>
>>
>> Viktor Klang
>> Software Architect, Java Platform Group
>> Oracle
>>
>> --
> Cheers,
> √
>
>
> Viktor Klang
> Software Architect, Java Platform Group
> Oracle
>
>

Reply via email to