JDK-8196106 : Support nested infinite or recursive flat mapped streams
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.stream
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2018-01-24
  • Updated: 2018-09-11
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Description
JDK-8075939 fixed the case where a flat map operation contains an infinite stream. However, it did not fix the case where there are nested flat map calls that contain infinite streams via recursion or explicitly.

For example, here is a recursive example from JDK-8189234:

    IntStream.rangeClosed(0, 1).
            flatMap(Test::map).
            limit(5).
            forEach(System.out::println);

    static IntStream getChildren() {
        return IntStream.rangeClosed(0, 1).
                flatMap(Test::map);
    }

    static IntStream map(int c) {
        return IntStream.concat(
                IntStream.of(c),
                getChildren());
    }

which will result in a StackOverlowError.

Here is another example using infinite streams:

    IntStream.rangeClosed(i - 1, i + 1).
            flatMap(c -> IntStream.generate(() -> 1).
                    flatMap(x -> IntStream.generate(() -> x))).
            limit(5).forEach(System.out::println);

which will result in an OutOfMemoryError.

To solve these issues will require a mechanism to propagate the enclosing stream's cancellation function (Sink::cancellationRequested) to nested streams. Such as a terminal operation forEachOrderedWithCancel that accepts a Predicate function to query the enclosing cancellation state.

This will also avoid an issue with the spliterator obtained from a stream (AbstractWrappingSpliterator and sub-classes) which has to buffer elements to avoid reporting more than one element via Spliterator::tryAdvance (independently the implementation can be improved by directly reporting the first element and buffering subsequent elements).