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
                recordE(Ee) {}
                Stream.iterate(newE(newE(newE(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(newE(newE(newE(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
                Exceptionjava.lang.NullPointerException:Cannotinvoke
                "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(newE(newE(newE(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(newE(newE(newE(null))), e ->e.e())<
                /span>
                .parallel()
                .distinct()
                .takeWhile(Objects::nonNull)
                .forEach(IO::println)
                ```
                ```java
                Exceptionjava.lang.NullPointerException:Cannotinvoke
                "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