Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8191939

Stream.concat behaves like terminal operation

    XMLWordPrintable

Details

    • CSR
    • Resolution: Approved
    • P4
    • 10
    • core-libs
    • None
    • behavioral
    • minimal
    • Existing behaviour is now explicitly specified.
    • Java API
    • SE

    Description

      Summary

      Stream concatenation using the Stream.concat method (and primitive variants) is underspecified with respect to binding to input stream sources.

      Problem

      The Stream.concat implementation needs to bind to the input stream sources (specifically bind to the spliterators) to obtain characteristics that can be efficiently merged with and applied to the resulting concatenated stream.

      Solution

      Specify that Stream.concat binds to the input stream sources and therefore subsequent modifications to the source sources may not be reflected in the concatenated result. In addition provide in an api note an alternative approach without such a restriction.

      Specification

      The text for Stream, IntStream, LongStream, and DoubleStream is almost identical

      diff -r 5c9179225e64 src/java.base/share/classes/java/util/stream/DoubleStream.java
      --- a/src/java.base/share/classes/java/util/stream/DoubleStream.java    Fri Nov 03 10:01:08 2017 -0700
      +++ b/src/java.base/share/classes/java/util/stream/DoubleStream.java    Mon Nov 27 15:20:44 2017 -0800
      @@ -1089,11 +1089,27 @@
            * streams is parallel.  When the resulting stream is closed, the close
            * handlers for both input streams are invoked.
            *
      +     * <p>This method operates on the two input streams and binds each stream
      +     * to its source.  As a result subsequent modifications to an input stream
      +     * source may not be reflected in the concatenated stream result.
      +     *
            * @implNote
            * Use caution when constructing streams from repeated concatenation.
            * Accessing an element of a deeply concatenated stream can result in deep
            * call chains, or even {@code StackOverflowError}.
            *
      +     * @apiNote
      +     * To preserve optimization opportunities this method binds each stream to
      +     * its source and accepts only two streams as parameters.  For example, the
      +     * exact size of the concatenated stream source can be computed if the exact
      +     * size of each input stream source is known.
      +     * To concatenate more streams without binding, or without nested calls to
      +     * this method, try creating a stream of streams and flat-mapping with the
      +     * identity function, for example:
      +     * <pre>{@code
      +     *     DoubleStream concat = Stream.of(s1, s2, s3, s4).flatMapToDouble(s -> s);
      +     * }</pre>
      +     *
            * @param a the first stream
            * @param b the second stream
            * @return the concatenation of the two input streams
      diff -r 5c9179225e64 src/java.base/share/classes/java/util/stream/IntStream.java
      --- a/src/java.base/share/classes/java/util/stream/IntStream.java   Fri Nov 03 10:01:08 2017 -0700
      +++ b/src/java.base/share/classes/java/util/stream/IntStream.java   Mon Nov 27 15:20:44 2017 -0800
      @@ -1081,11 +1081,27 @@
            * streams is parallel.  When the resulting stream is closed, the close
            * handlers for both input streams are invoked.
            *
      +     * <p>This method operates on the two input streams and binds each stream
      +     * to its source.  As a result subsequent modifications to an input stream
      +     * source may not be reflected in the concatenated stream result.
      +     *
            * @implNote
            * Use caution when constructing streams from repeated concatenation.
            * Accessing an element of a deeply concatenated stream can result in deep
            * call chains, or even {@code StackOverflowError}.
            *
      +     * @apiNote
      +     * To preserve optimization opportunities this method binds each stream to
      +     * its source and accepts only two streams as parameters.  For example, the
      +     * exact size of the concatenated stream source can be computed if the exact
      +     * size of each input stream source is known.
      +     * To concatenate more streams without binding, or without nested calls to
      +     * this method, try creating a stream of streams and flat-mapping with the
      +     * identity function, for example:
      +     * <pre>{@code
      +     *     IntStream concat = Stream.of(s1, s2, s3, s4).flatMapToInt(s -> s);
      +     * }</pre>
      +     *
            * @param a the first stream
            * @param b the second stream
            * @return the concatenation of the two input streams
      diff -r 5c9179225e64 src/java.base/share/classes/java/util/stream/LongStream.java
      --- a/src/java.base/share/classes/java/util/stream/LongStream.java  Fri Nov 03 10:01:08 2017 -0700
      +++ b/src/java.base/share/classes/java/util/stream/LongStream.java  Mon Nov 27 15:20:44 2017 -0800
      @@ -1086,11 +1086,27 @@
            * streams is parallel.  When the resulting stream is closed, the close
            * handlers for both input streams are invoked.
            *
      +     * <p>This method operates on the two input streams and binds each stream
      +     * to its source.  As a result subsequent modifications to an input stream
      +     * source may not be reflected in the concatenated stream result.
      +     *
            * @implNote
            * Use caution when constructing streams from repeated concatenation.
            * Accessing an element of a deeply concatenated stream can result in deep
            * call chains, or even {@code StackOverflowError}.
            *
      +     * @apiNote
      +     * To preserve optimization opportunities this method binds each stream to
      +     * its source and accepts only two streams as parameters.  For example, the
      +     * exact size of the concatenated stream source can be computed if the exact
      +     * size of each input stream source is known.
      +     * To concatenate more streams without binding, or without nested calls to
      +     * this method, try creating a stream of streams and flat-mapping with the
      +     * identity function, for example:
      +     * <pre>{@code
      +     *     LongStream concat = Stream.of(s1, s2, s3, s4).flatMapToLong(s -> s);
      +     * }</pre>
      +     *
            * @param a the first stream
            * @param b the second stream
            * @return the concatenation of the two input streams
      diff -r 5c9179225e64 src/java.base/share/classes/java/util/stream/Stream.java
      --- a/src/java.base/share/classes/java/util/stream/Stream.java  Fri Nov 03 10:01:08 2017 -0700
      +++ b/src/java.base/share/classes/java/util/stream/Stream.java  Mon Nov 27 15:20:44 2017 -0800
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
      + * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
        * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        *
        * This code is free software; you can redistribute it and/or modify it
      @@ -1341,6 +1341,10 @@
            * streams is parallel.  When the resulting stream is closed, the close
            * handlers for both input streams are invoked.
            *
      +     * <p>This method operates on the two input streams and binds each stream
      +     * to its source.  As a result subsequent modifications to an input stream
      +     * source may not be reflected in the concatenated stream result.
      +     *
            * @implNote
            * Use caution when constructing streams from repeated concatenation.
            * Accessing an element of a deeply concatenated stream can result in deep
      @@ -1349,6 +1353,18 @@
            * <p>Subsequent changes to the sequential/parallel execution mode of the
            * returned stream are not guaranteed to be propagated to the input streams.
            *
      +     * @apiNote
      +     * To preserve optimization opportunities this method binds each stream to
      +     * its source and accepts only two streams as parameters.  For example, the
      +     * exact size of the concatenated stream source can be computed if the exact
      +     * size of each input stream source is known.
      +     * To concatenate more streams without binding, or without nested calls to
      +     * this method, try creating a stream of streams and flat-mapping with the
      +     * identity function, for example:
      +     * <pre>{@code
      +     *     Stream<T> concat = Stream.of(s1, s2, s3, s4).flatMap(s -> s);
      +     * }</pre>
      +     *
            * @param <T> The type of stream elements
            * @param a the first stream
            * @param b the second stream

      Attachments

        Issue Links

          Activity

            People

              psandoz Paul Sandoz
              webbuggrp Webbug Group
              Stuart Marks
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: