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:
Patrick Concannon 2020-08-31 16:12:32 +01:00
parent dd89c92c50
commit 79d12507b3
10 changed files with 899 additions and 15 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2020, 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
@ -304,6 +304,31 @@ abstract class DoublePipeline<E_IN>
};
}
@Override
public final DoubleStream mapMulti(DoubleMapMultiConsumer mapper) {
Objects.requireNonNull(mapper);
return new StatelessOp<>(this, StreamShape.DOUBLE_VALUE,
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
@Override
Sink<Double> opWrapSink(int flags, Sink<Double> sink) {
return new Sink.ChainedDouble<>(sink) {
@Override
public void begin(long size) {
downstream.begin(-1);
}
@Override
@SuppressWarnings("unchecked")
public void accept(double t) {
mapper.accept(t, (DoubleConsumer) downstream);
}
};
}
};
}
@Override
public DoubleStream unordered() {
if (!isOrdered())

View file

@ -163,6 +163,43 @@ public interface DoubleStream extends BaseStream<Double, DoubleStream> {
*/
DoubleStream flatMap(DoubleFunction<? 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 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 #flatMap flatMap} on this stream,
* passing a function that behaves as follows. First, it calls the mapper function
* with a {@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 flatMap}.
*
* @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 Stream#mapMulti Stream.mapMulti
* @since 16
*/
default DoubleStream mapMulti(DoubleMapMultiConsumer mapper) {
Objects.requireNonNull(mapper);
return flatMap(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 of this stream. The
* elements are compared for equality according to
@ -1180,4 +1217,30 @@ public interface DoubleStream extends BaseStream<Double, DoubleStream> {
*/
DoubleStream build();
}
/**
* Represents an operation that accepts a {@code double}-valued argument
* and a DoubleConsumer, and returns no result. This functional interface is
* used by {@link DoubleStream#mapMulti(DoubleMapMultiConsumer) DoubleStream.mapMulti}
* to replace a double value with zero or more double values.
*
* <p>This is a <a href="../function/package-summary.html">functional interface</a>
* whose functional method is {@link #accept(double, DoubleConsumer)}.
*
* @see DoubleStream#mapMulti(DoubleMapMultiConsumer)
*
* @since 16
*/
@FunctionalInterface
interface DoubleMapMultiConsumer {
/**
* Replaces the given {@code value} with zero or more values by feeding the mapped
* values to the {@code dc} consumer.
*
* @param value the double value coming from upstream
* @param dc a {@code DoubleConsumer} accepting the mapped values
*/
void accept(double value, DoubleConsumer dc);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2020, 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
@ -338,6 +338,30 @@ abstract class IntPipeline<E_IN>
};
}
@Override
public final IntStream mapMulti(IntMapMultiConsumer mapper) {
Objects.requireNonNull(mapper);
return new StatelessOp<>(this, StreamShape.INT_VALUE,
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
@Override
Sink<Integer> opWrapSink(int flags, Sink<Integer> sink) {
return new Sink.ChainedInt<>(sink) {
@Override
public void begin(long size) {
downstream.begin(-1);
}
@Override
@SuppressWarnings("unchecked")
public void accept(int t) {
mapper.accept(t, (IntConsumer) downstream);
}
};
}
};
}
@Override
public IntStream unordered() {
if (!isOrdered())

View file

@ -164,6 +164,43 @@ public interface IntStream extends BaseStream<Integer, IntStream> {
*/
IntStream flatMap(IntFunction<? extends IntStream> 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 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 #flatMap flatMap} 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 flatMap}.
*
* @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 Stream#mapMulti Stream.mapMulti
* @since 16
*/
default IntStream mapMulti(IntMapMultiConsumer mapper) {
Objects.requireNonNull(mapper);
return flatMap(e -> {
SpinedBuffer.OfInt buffer = new SpinedBuffer.OfInt();
mapper.accept(e, buffer);
return StreamSupport.intStream(buffer.spliterator(), false);
});
}
/**
* Returns a stream consisting of the distinct elements of this stream.
*
@ -1173,4 +1210,30 @@ public interface IntStream extends BaseStream<Integer, IntStream> {
*/
IntStream build();
}
/**
* Represents an operation that accepts an {@code int}-valued argument
* and an IntConsumer, and returns no result. This functional interface is
* used by {@link IntStream#mapMulti(IntMapMultiConsumer) IntStream.mapMulti}
* to replace an int value with zero or more int values.
*
* <p>This is a <a href="../function/package-summary.html">functional interface</a>
* whose functional method is {@link #accept(int, IntConsumer)}.
*
* @see IntStream#mapMulti(IntMapMultiConsumer)
*
* @since 16
*/
@FunctionalInterface
interface IntMapMultiConsumer {
/**
* Replaces the given {@code value} with zero or more values by feeding the mapped
* values to the {@code ic} consumer.
*
* @param value the int value coming from upstream
* @param ic an {@code IntConsumer} accepting the mapped values
*/
void accept(int value, IntConsumer ic);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2020, 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
@ -320,6 +320,30 @@ abstract class LongPipeline<E_IN>
};
}
@Override
public final LongStream mapMulti(LongMapMultiConsumer mapper) {
Objects.requireNonNull(mapper);
return new LongPipeline.StatelessOp<>(this, StreamShape.LONG_VALUE,
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
@Override
Sink<Long> opWrapSink(int flags, Sink<Long> sink) {
return new Sink.ChainedLong<>(sink) {
@Override
public void begin(long size) {
downstream.begin(-1);
}
@Override
@SuppressWarnings("unchecked")
public void accept(long t) {
mapper.accept(t, (LongConsumer) downstream);
}
};
}
};
}
@Override
public LongStream unordered() {
if (!isOrdered())

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2020, 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
@ -164,6 +164,43 @@ public interface LongStream extends BaseStream<Long, LongStream> {
*/
LongStream flatMap(LongFunction<? extends LongStream> 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 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 #flatMap flatMap} 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 flatMap}.
*
* @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 Stream#mapMulti Stream.mapMulti
* @since 16
*/
default LongStream mapMulti(LongMapMultiConsumer mapper) {
Objects.requireNonNull(mapper);
return flatMap(e -> {
SpinedBuffer.OfLong buffer = new SpinedBuffer.OfLong();
mapper.accept(e, buffer);
return StreamSupport.longStream(buffer.spliterator(), false);
});
}
/**
* Returns a stream consisting of the distinct elements of this stream.
*
@ -1177,4 +1214,30 @@ public interface LongStream extends BaseStream<Long, LongStream> {
*/
LongStream build();
}
/**
* Represents an operation that accepts a {@code long}-valued argument
* and a LongConsumer, and returns no result. This functional interface is
* used by {@link LongStream#mapMulti(LongStream.LongMapMultiConsumer) LongStream.mapMulti}
* to replace a long value with zero or more long values.
*
* <p>This is a <a href="../function/package-summary.html">functional interface</a>
* whose functional method is {@link #accept(long, LongConsumer)}.
*
* @see LongStream#mapMulti(LongStream.LongMapMultiConsumer)
*
* @since 16
*/
@FunctionalInterface
interface LongMapMultiConsumer {
/**
* Replaces the given {@code value} with zero or more values by feeding the mapped
* values to the {@code lc} consumer.
*
* @param value the long value coming from upstream
* @param lc a {@code LongConsumer} accepting the mapped values
*/
void accept(long value, LongConsumer lc);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2020, 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
@ -257,7 +257,7 @@ abstract class ReferencePipeline<P_IN, P_OUT>
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
@Override
Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) {
return new Sink.ChainedReference<P_OUT, R>(sink) {
return new Sink.ChainedReference<>(sink) {
// true if cancellationRequested() has been called
boolean cancellationRequestedCalled;
@ -428,6 +428,103 @@ abstract class ReferencePipeline<P_IN, P_OUT>
};
}
@Override
public final <R> Stream<R> mapMulti(BiConsumer<? super P_OUT, ? super Consumer<R>> mapper) {
Objects.requireNonNull(mapper);
return new StatelessOp<>(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
@Override
Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) {
return new Sink.ChainedReference<>(sink) {
@Override
public void begin(long size) {
downstream.begin(-1);
}
@Override
@SuppressWarnings("unchecked")
public void accept(P_OUT u) {
mapper.accept(u, (Consumer<R>) downstream);
}
};
}
};
}
@Override
public final IntStream mapMultiToInt(BiConsumer<? super P_OUT, ? super IntConsumer> mapper) {
Objects.requireNonNull(mapper);
return new IntPipeline.StatelessOp<>(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
@Override
Sink<P_OUT> opWrapSink(int flags, Sink<Integer> sink) {
return new Sink.ChainedReference<>(sink) {
@Override
public void begin(long size) {
downstream.begin(-1);
}
@Override
@SuppressWarnings("unchecked")
public void accept(P_OUT u) {
mapper.accept(u, (IntConsumer)downstream);
}
};
}
};
}
@Override
public final LongStream mapMultiToLong(BiConsumer<? super P_OUT, ? super LongConsumer> mapper) {
Objects.requireNonNull(mapper);
return new LongPipeline.StatelessOp<>(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
@Override
Sink<P_OUT> opWrapSink(int flags, Sink<Long> sink) {
return new Sink.ChainedReference<>(sink) {
@Override
public void begin(long size) {
downstream.begin(-1);
}
@Override
@SuppressWarnings("unchecked")
public void accept(P_OUT u) {
mapper.accept(u, (LongConsumer) downstream);
}
};
}
};
}
@Override
public final DoubleStream mapMultiToDouble(BiConsumer<? super P_OUT, ? super DoubleConsumer> mapper) {
Objects.requireNonNull(mapper);
return new DoublePipeline.StatelessOp<>(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
@Override
Sink<P_OUT> opWrapSink(int flags, Sink<Double> sink) {
return new Sink.ChainedReference<>(sink) {
@Override
public void begin(long size) {
downstream.begin(-1);
}
@Override
@SuppressWarnings("unchecked")
public void accept(P_OUT u) {
mapper.accept(u, (DoubleConsumer) downstream);
}
};
}
};
}
@Override
public final Stream<P_OUT> peek(Consumer<? super P_OUT> action) {
Objects.requireNonNull(action);

View file

@ -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.