-
CSR
-
Resolution: Approved
-
P4
-
None
-
behavioral
-
minimal
-
Existing behaviour is now explicitly specified.
-
Java API
-
SE
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
- csr of
-
JDK-8181175 Stream.concat behaves like terminal operation
- Closed