mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8238286: Add new flatMap stream operation that is more amenable to pushing
This patch adds a new flatmap-like operation called mapMulti to the java.util.Stream class as well as the primitive variations of this operation i.e. mapMultiToInt, IntStream mapMulti, etc. Reviewed-by: psandoz, smarks
This commit is contained in:
parent
dd89c92c50
commit
79d12507b3
10 changed files with 899 additions and 15 deletions
|
@ -26,20 +26,17 @@ package java.util.stream;
|
|||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.DoubleConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.LongConsumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.ToDoubleFunction;
|
||||
|
@ -279,6 +276,7 @@ public interface Stream<T> extends BaseStream<T, Stream<T>> {
|
|||
* function to apply to each element which produces a stream
|
||||
* of new values
|
||||
* @return the new stream
|
||||
* @see #mapMulti
|
||||
*/
|
||||
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
|
||||
|
||||
|
@ -342,6 +340,210 @@ public interface Stream<T> extends BaseStream<T, Stream<T>> {
|
|||
*/
|
||||
DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);
|
||||
|
||||
/**
|
||||
* Returns a stream consisting of the results of replacing each element of
|
||||
* this stream with multiple elements, specifically zero or more elements.
|
||||
* Replacement is performed by applying the provided mapping function to each
|
||||
* element in conjunction with a {@linkplain Consumer consumer} argument
|
||||
* that accepts replacement elements. The mapping function calls the consumer
|
||||
* zero or more times to provide the replacement elements.
|
||||
*
|
||||
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
|
||||
* operation</a>.
|
||||
*
|
||||
* <p>If the {@linkplain Consumer consumer} argument is used outside the scope of
|
||||
* its application to the mapping function, the results are undefined.
|
||||
*
|
||||
* @implSpec
|
||||
* The default implementation invokes {@link #flatMap flatMap} on this stream,
|
||||
* passing a function that behaves as follows. First, it calls the mapper function
|
||||
* with a {@code Consumer} that accumulates replacement elements into a newly created
|
||||
* internal buffer. When the mapper function returns, it creates a stream from the
|
||||
* internal buffer. Finally, it returns this stream to {@code flatMap}.
|
||||
*
|
||||
* @apiNote
|
||||
* This method is similar to {@link #flatMap flatMap} in that it applies a one-to-many
|
||||
* transformation to the elements of the stream and flattens the result elements
|
||||
* into a new stream. This method is preferable to {@code flatMap} in the following
|
||||
* circumstances:
|
||||
* <ul>
|
||||
* <li>When replacing each stream element with a small (possibly zero) number of
|
||||
* elements. Using this method avoids the overhead of creating a new Stream instance
|
||||
* for every group of result elements, as required by {@code flatMap}.</li>
|
||||
* <li>When it is easier to use an imperative approach for generating result
|
||||
* elements than it is to return them in the form of a Stream.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>If a lambda expression is provided as the mapper function argument, additional type
|
||||
* information maybe be necessary for proper inference of the element type {@code <R>} of
|
||||
* the returned stream. This can be provided in the form of explicit type declarations for
|
||||
* the lambda parameters or as an explicit type argument to the {@code mapMulti} call.
|
||||
*
|
||||
* <p><b>Examples</b>
|
||||
*
|
||||
* <p>Given a stream of {@code Number} objects, the following
|
||||
* produces a list containing only the {@code Integer} objects:
|
||||
* <pre>{@code
|
||||
* Stream<Number> numbers = ... ;
|
||||
* List<Integer> integers = numbers.<Integer>mapMulti((number, consumer) -> {
|
||||
* if (number instanceof Integer)
|
||||
* consumer.accept((Integer) number);
|
||||
* })
|
||||
* .collect(Collectors.toList());
|
||||
* }</pre>
|
||||
*
|
||||
* <p>If we have an {@code Iterable<Object>} and need to recursively expand its elements
|
||||
* that are themselves of type {@code Iterable}, we can use {@code mapMulti} as follows:
|
||||
* <pre>{@code
|
||||
* class C {
|
||||
* static void expandIterable(Object e, Consumer<Object> c) {
|
||||
* if (e instanceof Iterable) {
|
||||
* for (Object ie: (Iterable<?>) e) {
|
||||
* expandIterable(ie, c);
|
||||
* }
|
||||
* } else if (e != null) {
|
||||
* c.accept(e);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* public static void main(String[] args) {
|
||||
* Stream<Object> stream = ...;
|
||||
* Stream<Object> expandedStream = stream.mapMulti(C::expandIterable);
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @param <R> The element type of the new stream
|
||||
* @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
|
||||
* <a href="package-summary.html#Statelessness">stateless</a>
|
||||
* function that generates replacement elements
|
||||
* @return the new stream
|
||||
* @see #flatMap flatMap
|
||||
* @since 16
|
||||
*/
|
||||
default <R> Stream<R> mapMulti(BiConsumer<? super T, ? super Consumer<R>> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return flatMap(e -> {
|
||||
SpinedBuffer<R> buffer = new SpinedBuffer<>();
|
||||
mapper.accept(e, buffer);
|
||||
return StreamSupport.stream(buffer.spliterator(), false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@code IntStream} consisting of the results of replacing each
|
||||
* element of this stream with multiple elements, specifically zero or more
|
||||
* elements.
|
||||
* Replacement is performed by applying the provided mapping function to each
|
||||
* element in conjunction with a {@linkplain IntConsumer consumer} argument
|
||||
* that accepts replacement elements. The mapping function calls the consumer
|
||||
* zero or more times to provide the replacement elements.
|
||||
*
|
||||
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
|
||||
* operation</a>.
|
||||
*
|
||||
* <p>If the {@linkplain IntConsumer consumer} argument is used outside the scope of
|
||||
* its application to the mapping function, the results are undefined.
|
||||
*
|
||||
* @implSpec
|
||||
* The default implementation invokes {@link #flatMapToInt flatMapToInt} on this stream,
|
||||
* passing a function that behaves as follows. First, it calls the mapper function
|
||||
* with an {@code IntConsumer} that accumulates replacement elements into a newly created
|
||||
* internal buffer. When the mapper function returns, it creates an {@code IntStream} from
|
||||
* the internal buffer. Finally, it returns this stream to {@code flatMapToInt}.
|
||||
*
|
||||
* @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
|
||||
* <a href="package-summary.html#Statelessness">stateless</a>
|
||||
* function that generates replacement elements
|
||||
* @return the new stream
|
||||
* @see #mapMulti mapMulti
|
||||
* @since 16
|
||||
*/
|
||||
default IntStream mapMultiToInt(BiConsumer<? super T, ? super IntConsumer> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return flatMapToInt(e -> {
|
||||
SpinedBuffer.OfInt buffer = new SpinedBuffer.OfInt();
|
||||
mapper.accept(e, buffer);
|
||||
return StreamSupport.intStream(buffer.spliterator(), false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code LongStream} consisting of the results of replacing each
|
||||
* element of this stream with multiple elements, specifically zero or more
|
||||
* elements.
|
||||
* Replacement is performed by applying the provided mapping function to each
|
||||
* element in conjunction with a {@linkplain LongConsumer consumer} argument
|
||||
* that accepts replacement elements. The mapping function calls the consumer
|
||||
* zero or more times to provide the replacement elements.
|
||||
*
|
||||
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
|
||||
* operation</a>.
|
||||
*
|
||||
* <p>If the {@linkplain LongConsumer consumer} argument is used outside the scope of
|
||||
* its application to the mapping function, the results are undefined.
|
||||
*
|
||||
* @implSpec
|
||||
* The default implementation invokes {@link #flatMapToLong flatMapToLong} on this stream,
|
||||
* passing a function that behaves as follows. First, it calls the mapper function
|
||||
* with a {@code LongConsumer} that accumulates replacement elements into a newly created
|
||||
* internal buffer. When the mapper function returns, it creates a {@code LongStream} from
|
||||
* the internal buffer. Finally, it returns this stream to {@code flatMapToLong}.
|
||||
*
|
||||
* @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
|
||||
* <a href="package-summary.html#Statelessness">stateless</a>
|
||||
* function that generates replacement elements
|
||||
* @return the new stream
|
||||
* @see #mapMulti mapMulti
|
||||
* @since 16
|
||||
*/
|
||||
default LongStream mapMultiToLong(BiConsumer<? super T, ? super LongConsumer> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return flatMapToLong(e -> {
|
||||
SpinedBuffer.OfLong buffer = new SpinedBuffer.OfLong();
|
||||
mapper.accept(e, buffer);
|
||||
return StreamSupport.longStream(buffer.spliterator(), false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code DoubleStream} consisting of the results of replacing each
|
||||
* element of this stream with multiple elements, specifically zero or more
|
||||
* elements.
|
||||
* Replacement is performed by applying the provided mapping function to each
|
||||
* element in conjunction with a {@linkplain DoubleConsumer consumer} argument
|
||||
* that accepts replacement elements. The mapping function calls the consumer
|
||||
* zero or more times to provide the replacement elements.
|
||||
*
|
||||
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
|
||||
* operation</a>.
|
||||
*
|
||||
* <p>If the {@linkplain DoubleConsumer consumer} argument is used outside the scope of
|
||||
* its application to the mapping function, the results are undefined.
|
||||
*
|
||||
* @implSpec
|
||||
* The default implementation invokes {@link #flatMapToDouble flatMapToDouble} on this stream,
|
||||
* passing a function that behaves as follows. First, it calls the mapper function
|
||||
* with an {@code DoubleConsumer} that accumulates replacement elements into a newly created
|
||||
* internal buffer. When the mapper function returns, it creates a {@code DoubleStream} from
|
||||
* the internal buffer. Finally, it returns this stream to {@code flatMapToDouble}.
|
||||
*
|
||||
* @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
|
||||
* <a href="package-summary.html#Statelessness">stateless</a>
|
||||
* function that generates replacement elements
|
||||
* @return the new stream
|
||||
* @see #mapMulti mapMulti
|
||||
* @since 16
|
||||
*/
|
||||
default DoubleStream mapMultiToDouble(BiConsumer<? super T, ? super DoubleConsumer> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return flatMapToDouble(e -> {
|
||||
SpinedBuffer.OfDouble buffer = new SpinedBuffer.OfDouble();
|
||||
mapper.accept(e, buffer);
|
||||
return StreamSupport.doubleStream(buffer.spliterator(), false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stream consisting of the distinct elements (according to
|
||||
* {@link Object#equals(Object)}) of this stream.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue