org.paumard.streams.StreamsUtils Maven / Gradle / Ivy
Show all versions of streams-utils Show documentation
/*
* Copyright (C) 2015 José Paumard
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.paumard.streams;
import org.paumard.spliterators.*;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import static java.util.function.Function.identity;
/**
* A factory class used to create streams from other streams. There are currently seven ways of rearranging streams.
*
* Here is a first example of what can be done:
* {@code
* // Create an example Stream
* Stream stream = Stream.of("a0", "a1", "a2", "a3");
* Stream> groupingStream = StreamsUtils.group(stream, 2);
* List> collect = groupingStream.map(st -> st.collect(Collectors.toList())).collect(Collectors.toList());
* // The collect list is [["a0", "a1"]["a2", "a3"]]
* }
* See the documentation of each factory method for more information.
*
* @author José Paumard
* @since 0.1
*/
public class StreamsUtils {
/**
* Generates a stream by repeating the elements of the provided stream forever. This stream is not bounded.
* {@code
* Stream stream = Stream.of("tick", "tock");
* Stream cyclingStream = StreamsUtils.cycle(stream);
* List collect = cyclingStream.limit(9).collect(Collectors.toList());
* // The collect list is ["tick", "tock", "tick", "tock", "tick", "tock", "tick", "tock", "tick"]
* }
* The returned spliterator is ORDERED
.
*
* @param stream The stream to cycle on. Will throw a NullPointerException
if null
.
* @param The type of the elements of the provided stream.
* @return A cycling stream.
*/
public static Stream cycle(Stream stream) {
Objects.requireNonNull(stream);
CyclingSpliterator spliterator = CyclingSpliterator.of(stream.spliterator());
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close).flatMap(identity());
}
/**
* Generates a stream by regrouping the elements of the provided stream and putting them in a substream. The number
* of elements regrouped is the groupingFactor
.
* Example:
* {@code
* Stream stream = Stream.of("a0", "a1", "a2", "a3");
* Stream> groupingStream = StreamsUtils.group(stream, 2);
* List> collect = groupingStream.map(st -> st.collect(Collectors.toList())).collect(Collectors.toList());
* // The collect list is [["a0", "a1"]["a2", "a3"]]
* }
* If the provided stream is empty, then the returned stream contains an empty stream.
* The groupingFactor
should be greater of equals than 2. A grouping factor of 0 does not make
* sense. A grouping factor of 1 is in fact a mapping with a Stream::of
. An
* IllegalArgumentException
will be thrown if a non valid groupingFactor
is provided.
* An IllegalArgumentException
will also be thrown if the provided stream is not ORDERED
* The returned stream has the same characteristics as the provided stream, and is thus ORDERED
.
* All the returned substreams are guaranteed to produce groupingFactor
elements. So there might be
* elements from the provided stream that will not be consumed in the grouped stream.
*
* @param stream The stream to be grouped. Will throw a NullPointerException
if null
.
* @param groupingFactor The grouping factor, should be greater of equal than 2.
* @param The type of the elements of the provided stream.
* @return A grouped stream of streams.
*/
public static Stream> group(Stream stream, int groupingFactor) {
Objects.requireNonNull(stream);
GroupingSpliterator spliterator = GroupingSpliterator.of(stream.spliterator(), groupingFactor);
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
/**
* Generates a stream by regrouping the elements of the provided stream and putting them in a substream.
* This grouping operation scans the elements of the stream using the open
predicate. If this
* predicate is true, then it begins to add the elements of the stream in a substream. It will continue to add
* them until an element that matches the close
predicate is met.
* The basic group operation includes the opening and the closing elements in the substreams. You can also
* provide two booleans if you need to customize this behavior.
* Example:
* {@code
* Stream stream = Stream.of("o", "a0", "a1", "a2", "c", "a3", "a4, "o", "a5", "c");
* Stream> groupingStream = StreamsUtils.group(stream, "o"::equals, "c"::equals);
* List> collect = groupingStream.map(st -> st.collect(Collectors.toList())).collect(Collectors.toList());
* // The collect list is [["o", "a0", "a1", "a2", "c"]["o", "a5", "c"]]
* }
* If the provided stream is empty, then the returned stream contains an empty stream.
* An IllegalArgumentException
will also be thrown if the provided stream is not ORDERED
* The returned stream has the same characteristics as the provided stream, and is thus ORDERED
.
* A {@code {@link NullPointerException}} is thrown if the stream to be grouped or one of the predicate is null.
*
* @param stream The stream to be grouped. Will throw a NullPointerException
if null
.
* @param open The predicate used to check for an opening element.
* @param close The predicate used to check for an closing element.
* @param The type of the elements of the provided stream.
* @return A grouped stream of streams.
*/
public static Stream> group(Stream stream, Predicate super E> open, Predicate super E> close) {
Objects.requireNonNull(stream);
Objects.requireNonNull(open);
Objects.requireNonNull(close);
return group(stream, open, true, close, true);
}
/**
* Generates a stream by regrouping the elements of the provided stream and putting them in a substream.
* This grouping operation scans the elements of the stream using the splitter predicate. When this predicate
* returns true, then the elements of the stream are accumulated in a subtream, until the splitter predicate
* is true again. In this case, a new substream is created, until the elements of the provided stream are
* exhausted.
* In the case where several consecutive splitting elements are met, no empty stream is generated.
* The splitting element is added only once to the next substream if needed.
* The boolean included
controls whether the splitting element is added to the substreams or not.
* A {@code {@link NullPointerException}} is thrown if the stream to be grouped or the splitter is null.
*
* @param stream The stream to be grouped. Will throw a NullPointerException
if null
.
* @param splitter The predicate used to check for an splitting element.
* @param included if true: includes the splitting element at the beginning of each substream
* @param The type of the elements of the provided stream.
* @return A grouped stream of streams.
*/
public static Stream> group(Stream stream, Predicate super E> splitter, boolean included) {
Objects.requireNonNull(stream);
Objects.requireNonNull(splitter);
GroupingOnSplittingSpliterator spliterator = GroupingOnSplittingSpliterator.of(stream.spliterator(), splitter, included);
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
/**
* Generates a stream by regrouping the elements of the provided stream and putting them in a substream.
* This grouping operation scans the elements of the stream using the splitter predicate. When this predicate
* returns true, then the elements of the stream are accumulated in a subtream, until the splitter predicate
* is true again. In this case, a new substream is created, until the elements of the provided stream are
* exhausted.
* In this case, the splitting element is not added to the substream.
* In the case where several consecutive splitting elements are met, no empty stream is generated.
* A {@code {@link NullPointerException}} is thrown if the stream to be grouped or the splitter is null.
*
* @param stream The stream to be grouped. Will throw a NullPointerException
if null
.
* @param splitter The predicate used to check for an splitting element.
* @param The type of the elements of the provided stream.
* @return A grouped stream of streams.
*/
public static Stream> group(Stream stream, Predicate super E> splitter) {
Objects.requireNonNull(stream);
Objects.requireNonNull(splitter);
return group(stream, splitter, true);
}
/**
* Generates a stream by regrouping the elements of the provided stream and putting them in a substream.
* This grouping operation scans the elements of the stream using the open
predicate. If this
* predicate is true, then it begins to add the elements of the stream in a substream. It will continue to add
* them until an element that matches the close
predicate is met.
* Adding the opening and the closing elements is controlled by the two boolean parameters
* openingElementIncluded
and closingElementIncluded
.
* Example:
* {@code
* Stream stream = Stream.of("o", "a0", "a1", "a2", "c", "a3", "a4, "o", "a5", "c");
* Stream> groupingStream = StreamsUtils.group(stream, "o"::equals, false, "c"::equals, false);
* List> collect = groupingStream.map(st -> st.collect(Collectors.toList())).collect(Collectors.toList());
* // The collect list is [["a0", "a1", "a2"]["a5"]]
* }
* If the provided stream is empty, then the returned stream contains an empty stream.
* An IllegalArgumentException
will also be thrown if the provided stream is not ORDERED
* The returned stream has the same characteristics as the provided stream, and is thus ORDERED
.
* A {@code {@link NullPointerException}} is thrown if the stream to be grouped or one of the predicate is null.
*
* @param stream The stream to be grouped. Will throw a NullPointerException
if null
.
* @param open The predicate used to check for an opening element.
* @param openingElementIncluded if true : includes the opening element in each substream
* @param close The predicate used to check for an closing element.
* @param closingElementIncluded if true : includes the closing element in each substream
* @param The type of the elements of the provided stream.
* @return A grouped stream of streams.
*/
public static Stream> group(
Stream stream,
Predicate super E> open, boolean openingElementIncluded,
Predicate super E> close, boolean closingElementIncluded) {
Objects.requireNonNull(stream);
Objects.requireNonNull(open);
Objects.requireNonNull(close);
GroupingOnGatingSpliterator spliterator = GroupingOnGatingSpliterator.of(stream.spliterator(), open, openingElementIncluded, close, closingElementIncluded);
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
/**
* Generates a stream by repeating the elements of the provided stream. The number of times an element is
* repeated is given by the repeating factor.
*
Example:
* {@code
* Stream stream = Stream.of("a0", "a1", "a2", "a3");
* Stream repeatingStream = StreamsUtils.repeat(stream, 3);
* List collect = repeatingStream.collect(Collectors.toList());
* // The collect list is ["a0", "a0", "a0", "a1", "a1", "a1", "a2", "a2", "a2", "a3", "a3", "a3"]
* }
* If the provided stream is empty, then the returned stream is also empty.
* The repeatingFactor
should be greater of equals than 2. A repeating factor of 0 does not make
* sense. A repeating factor of 1 is in fact the identity operation. An
* IllegalArgumentException
will be thrown if a non valid repeatingFactor
is provided.
* An IllegalArgumentException
will be thrown if a non SIZED
stream is provided.
* Believe me, trying to repeat an infinite stream is not a good idea.
* The repeating of the provided stream should no lead to the producing of more than Long.MAX_VALUE
.
* Weird effects will occur in that case.
* A NullPointerException
is thrown if the provided stream is null.
* The returned stream is ORDERED
.
*
* @param stream The stream to be repeated. Will throw a NullPointerException
if null
.
* @param repeatingFactor The repeating factor, should be greater of equal than 2.
* @param The type of the elements of the provided stream.
* @return A repeating stream.
*/
public static Stream repeat(Stream stream, int repeatingFactor) {
Objects.requireNonNull(stream);
RepeatingSpliterator spliterator = RepeatingSpliterator.of(stream.spliterator(), repeatingFactor);
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
/**
* Generates a stream by grouping the elements of the provided stream, and by advancing one by one the first
* element of the next substream. The number of elements of the substreams is the rolling factor.
*
Example:
* {@code
* Stream stream = Stream.of("a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7");
* Stream> rollingStream = StreamsUtils.roll(stream, 3);
* List> collect = rollingStream.map(st -> st.collect(Collectors.toList())).collect(Collectors.toList());
* // The collect list is [["a0", "a1", "a2"],
* ["a1", "a2", "a3"],
* ["a2", "a3", "a4"],
* ["a3", "a4", "a5"],
* ["a4", "a5", "a6"],
* ["a5", "a6", "a7"]]
* }
* If the provided stream is empty, then the returned stream contains an empty stream.
* The rollingFactor
should be greater of equals than 2. A rolling factor of 0 does not make
* sense. A rolling factor of 1 is in fact a mapping with a Stream::of
. An
* IllegalArgumentException
will be thrown if a non valid rollingFactor
is provided.
* An IllegalArgumentException
will also be thrown is a non ORDERED
stream is
* provided.
* The returned stream has the same characteristics as the provided stream, and is thus ORDERED
.
* All the returned substreams are guaranteed to produce rollingFactor
elements. So there might be
* elements from the provided stream that will not be consumed in the grouped stream.
* A NullPointerException
is thrown if the provided stream is null.
* An IllegalArgumentException
is thrown if the rollingFactor
is less than 2,
* or if the provided stream is non-ordered.
*
* @param stream The stream to be rolled. Will throw a NullPointerException
if null
.
* @param rollingFactor The rolling factor, should be greater of equal than 2.
* @param The type of the elements of the provided stream.
* @return A rolling stream of streams.
*/
public static Stream> roll(Stream stream, int rollingFactor) {
Objects.requireNonNull(stream);
RollingSpliterator spliterator = RollingSpliterator.of(stream.spliterator(), rollingFactor);
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
/**
* Generates a stream by taking one element of the provided streams at a time, and putting them in a substream.
*
Example:
* {@code
* Stream stream0 = Stream.of("a00", "a01", "a02", "a03");
* Stream stream1 = Stream.of("a10", "a11", "a12", "a13");
* Stream stream2 = Stream.of("a20", "a21", "a22", "a23");
* Stream stream3 = Stream.of("a30", "a31", "a32", "a33");
* Stream> traversingStream = StreamsUtils.traverse(stream0, stream1, stream2, stream3);
* List> collect = traversingStream.map(st -> st.collect(Collectors.toList())).collect(Collectors.toList());
* // The collect list is [["a00", "a10", "a20", "a30"],
* ["a01", "a11", "a21", "a31"],
* ["a02", "a12", "a22", "a32"],
* ["a03", "a13", "a23", "a33"]]
* }
* An IllegalArgumentException
is thrown if there is only one stream provided in the varargs. In
* that case, the traversing would be a mapping with Stream::of
.
* An IllegalArgumentException
is also thrown if one of the provided streams is not ORDERED
.
* The characteristics of the returned stream is the bitwise AND
of all the characteristics of
* the provided streams. In most of the cases, all these streams will share the same characteristics, so in this
* case it will be the same as well. The returned stream is thus ORDERED
.
* A NullPointerException
is thrown if one of the provided streams is null.
*
* @param streams The streams to be traversed. Will throw a NullPointerException
if null
.
* @param The type of the elements of the provided stream.
* @return A traversing stream of streams.
*/
@SafeVarargs
public static Stream> traverse(Stream... streams) {
Arrays.stream(streams).forEach(Objects::requireNonNull);
@SuppressWarnings("unchecked")
Spliterator[] spliterators = Stream.of(streams).map(Stream::spliterator).toArray(Spliterator[]::new);
TraversingSpliterator spliterator = TraversingSpliterator.of(spliterators);
return StreamSupport.stream(spliterator, Arrays.stream(streams).allMatch(Stream::isParallel))
.onClose(() -> Arrays.stream(streams).forEach(Stream::close));
}
/**
* Generates a stream by taking one element of the provided streams at a time, and putting them in the
* resulting stream.
*
Example:
* {@code
* Stream stream0 = Stream.of("a00", "a01", "a02");
* Stream stream1 = Stream.of("a10", "a11", "a12");
* Stream stream2 = Stream.of("a20", "a21", "a22");
* Stream> weavingStream = StreamsUtils.traverse(stream0, stream1, stream2);
* List collect = weavingStream.map(st -> st.collect(Collectors.toList()).collect(Collectors.toList());
* // The collect list is ["a00", "a10", "a20", "a01", "a11", "a21", "a02", "a12", "a22"]
* }
* An IllegalArgumentException
is thrown if there is only one stream provided in the varargs. In
* that casse, the traversing would be a mapping with Stream::of
.
* An IllegalArgumentException
is also thrown if one of the provided streams is not ORDERED
.
* The characteristics of the returned stream is the bitwise AND
of all the characteristics of
* the provided streams. In most of the cases, all these streams will share the same characteristics, so in this
* case it will be the same as well. The returned stream is thus ORDERED
.
* The returned stream will stop producing elements as soon as one of the provided stream stops to do so.
* So some of the elements of the provided streams might not be consumed.
* A NullPointerException
is thrown if one of the provided streams is null.
*
* @param streams The streams to be weaved. Will throw a NullPointerException
if null
.
* @param The type of the elements of the provided stream.
* @return A weaved stream.
*/
@SafeVarargs
public static Stream weave(Stream... streams) {
Arrays.stream(streams).forEach(Objects::requireNonNull);
@SuppressWarnings("unchecked")
Spliterator[] spliterators = Stream.of(streams).map(Stream::spliterator).toArray(Spliterator[]::new);
WeavingSpliterator spliterator = WeavingSpliterator.of(spliterators);
return StreamSupport.stream(spliterator, Arrays.stream(streams).allMatch(Stream::isParallel))
.onClose(() -> Arrays.stream(streams).forEach(Stream::close));
}
/**
* Generates a stream by taking one element at a time from each of the provided streams, and transforming them
* using the provided bifunction.
*
Example:
* {@code
* Stream stream0 = Stream.of("a", "b", "c", "d");
* Stream stream1 = Stream.of(0, 1, 2, 3);
* Bifunction zipper = (s, i) -> s + "-" + i;
* Stream zippingStream = StreamsUtils.zip(stream0, stream1, zipper);
* List collect = zippingStream.collect(Collectors.toList());
* // The collect list is ["a-0", "b-1", "c-2", "d-3"]
* }
* The characteristics of the returned spliterator is the bitwise AND
of the characteristics of
* the provided streams. Those streams should have the same characteristics, so there will be no change on
* this point.
* The returned stream will stop producing elements as soon as one of the provided stream stops to do so.
* So some of the elements of the provided streams might not be consumed.
* A NullPointerException
will be thrown if the zipper
generates a null value. So
* the returned stream is guaranteed not to have null values.
* In case you cannot be sure that your zipper returns null
, then you can provide a
* zipper
than wraps its result in an Optional
(using the
* Optional.ofNullable()
factory method), and flat map the returned stream. Your nulls will then
* be silently removed from the stream.
*
* @param stream1 The first stream to be zipped. Will throw a NullPointerException
if null
.
* @param stream2 The second stream to be zipped. Will throw a NullPointerException
if null
.
* @param zipper The bifunction used to transform the elements of the two streams.
* Will throw a NullPointerException
if null
.
* @param The type of the elements of the first provided stream.
* @param The type of the elements of the second provided stream.
* @param The type of the elements of the returned stream.
* @return A zipped stream.
*/
public static Stream zip(Stream stream1, Stream stream2, BiFunction super E1, ? super E2, ? extends R> zipper) {
Objects.requireNonNull(stream1);
Objects.requireNonNull(stream2);
Objects.requireNonNull(zipper);
ZippingSpliterator.Builder builder = new ZippingSpliterator.Builder<>();
ZippingSpliterator spliterator =
builder.with(stream1.spliterator())
.and(stream2.spliterator())
.mergedBy(zipper)
.build();
return StreamSupport.stream(spliterator, stream1.isParallel() && stream2.isParallel())
.onClose(() -> {
stream1.close();
stream2.close();
});
}
/**
* Generates a stream by validating the elements of an input stream one by one using the provided predicate.
* An element of the input stream is said to be valid if the provided predicate returns true for this element.
* A valid element is replaced in the returned stream by the application of the provided function for valid
* elements. A non-valid element is replaced by the other function.
* A NullPointerException
will be thrown if one of the provided elements is null.
*
* @param stream the stream to be validated. Will throw a NullPointerException
if null
.
* @param validator the predicate used to validate the elements of the stream.
* Will throw a NullPointerException
if null
.
* @param transformingIfValid the function applied to the valid elements.
* Will throw a NullPointerException
if null
.
* @param transformingIfNotValid the function applied to the non-valid elements.
* Will throw a NullPointerException
if null
.
* @param the type of the elements of the input stream.
* @param the type of the elements of the returned stream.
* @return the validated and transformed stream.
*/
public static Stream validate(Stream stream, Predicate super E> validator,
Function super E, ? extends R> transformingIfValid, Function super E, ? extends R> transformingIfNotValid) {
Objects.requireNonNull(stream);
Objects.requireNonNull(validator);
Objects.requireNonNull(transformingIfValid);
Objects.requireNonNull(transformingIfNotValid);
ValidatingSpliterator.Builder builder = new ValidatingSpliterator.Builder<>();
ValidatingSpliterator spliterator = builder.with(stream.spliterator())
.validatedBy(validator)
.withValidFunction(transformingIfValid)
.withNotValidFunction(transformingIfNotValid)
.build();
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
/**
* Generates a stream by validating the elements of an input stream one by one using the provided predicate.
* An element of the input stream is said to be valid if the provided predicate returns true for this element.
* A valid element is transmitted to the returned stream without any transformation. A non-valid element is
* replaced by the application of the provided unary operator.
* This function calls the general version of validate()
with special parameters.
* A NullPointerException
will be thrown if one of the provided elements is null.
*
* @param stream the stream to be validated. Will throw a NullPointerException
if null
.
* @param validator the predicate used to validate the elements of the stream.
* Will throw a NullPointerException
if null
.
* @param transformingIfNotValid the operator applied to the non-valid elements.
* Will throw a NullPointerException
if null
.
* @param the type of the stream and the returned stream.
* @return the validated and transformed stream.
*/
public static Stream validate(Stream stream, Predicate super E> validator, UnaryOperator transformingIfNotValid) {
return validate(stream, validator, Function.identity(), transformingIfNotValid);
}
/**
* Generates a stream identical to the provided stream until the interruptor predicate is false for one element.
* At that time, the returned stream stops.
* A NullPointerException
will be thrown if the provided stream of the interruptor predicate is null.
* If you are using Java 9, then yo should use Stream.takeWhile(Predicate)
.
*
* @param stream the input stream. Will throw a NullPointerException
if null
.
* @param interruptor the predicate applied to the elements of the input stream.
* Will throw a NullPointerException
if null
.
* @param the type of the stream and the returned stream.
* @return a stream that is a copy of the input stream, until the interruptor becomes false.
*/
public static Stream interrupt(Stream stream, Predicate super E> interruptor) {
Objects.requireNonNull(stream);
Objects.requireNonNull(interruptor);
InterruptingSpliterator spliterator = InterruptingSpliterator.of(stream.spliterator(), interruptor);
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
/**
* Generates a stream that does not generate any element, until the validator becomes true for an element of
* the provided stream. From this point, the returns stream is identical to the provided stream.
* If you are using Java 9, then yo should use Stream.dropWhile(Predicate)
.
* A NullPointerException
will be thrown if the provided stream of the validator predicate is null.
*
* @param stream the input stream. Will throw a NullPointerException
if null
.
* @param validator the predicate applied to the elements of the input stream.
* Will throw a NullPointerException
if null
.
* @param the type of the stream and the returned stream.
* @return a stream that starts when the validator becomes true.
*/
public static Stream gate(Stream stream, Predicate super E> validator) {
Objects.requireNonNull(stream);
Objects.requireNonNull(validator);
GatingSpliterator spliterator = GatingSpliterator.of(stream.spliterator(), validator);
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
/**
* Generates a stream that is computed from a provided stream following two steps.
* The first steps consists in building a rolling stream with the rollingFactor
passed as
* a parameter. This rolling stream is the same the one built using the roll()
method.
*
* Then each substream is collected using the collector passed as the third parameter.
* The result is set up in a stream that has the same number of elements as the provided stream,
* minus the size of the window width, to preserve consistency of each collection.
* A NullPointerException
will be thrown if the provided stream or the collector is null.
*
* @param stream the processed stream
* @param rollingFactor the size of the window to apply the collector on
* @param collector the collector to be applied
* @param the type of the provided stream
* @param the type of the returned stream
* @return a stream in which each value is the collection of the provided stream
*/
public static Stream shiftingWindowCollect(Stream stream, int rollingFactor, Collector super E, ?, ? extends T> collector) {
Objects.requireNonNull(stream);
Objects.requireNonNull(collector);
return roll(stream, rollingFactor).map(str -> str.collect(collector));
}
/**
* Generates a stream that is computed from a provided stream following two steps.
* The first steps maps this stream to an IntStream
that is then rolled following
* the same principle as the roll()
method. This steps builds a Stream<IntStream>
.
*
* Then the average()
method is called on each IntStream
using a mapper, and a
* DoubleStream
of averages is returned.
* The resulting stream has the same number of elements as the provided stream,
* minus the size of the window width, to preserve consistency of each collection.
* A NullPointerException
will be thrown if the provided stream or the mapper is null.
*
* @param stream the processed stream
* @param rollingFactor the size of the window to apply the collector on
* @param mapper the mapper applied
* @param the type of the provided stream
* @return a stream in which each value is the average of the provided stream
*/
public static DoubleStream shiftingWindowAveragingInt(Stream stream, int rollingFactor, ToIntFunction super E> mapper) {
Objects.requireNonNull(stream);
Objects.requireNonNull(mapper);
IntStream intStream = stream.mapToInt(mapper);
return shiftingWindowAveragingInt(intStream, rollingFactor);
}
/**
* Generates a stream that is computed from a provided int stream by first rolling it in the same
* way as the roll()
method does. The average is then computed on each substream, to
* form the final double stream. No boxing / unboxing is conducted in the process.
*
The resulting stream has the same number of elements as the provided stream,
* minus the size of the window width, to preserve consistency of each collection.
* A NullPointerException
will be thrown if the provided stream is null.
*
* @param intStream the processed stream
* @param rollingFactor the size of the window to apply the collector on
* @return a stream in which each value is the collection of the provided stream
*/
public static DoubleStream shiftingWindowAveragingInt(IntStream intStream, int rollingFactor) {
Objects.requireNonNull(intStream);
RollingOfIntSpliterator ofIntSpliterator = RollingOfIntSpliterator.of(intStream.spliterator(), rollingFactor);
return StreamSupport.stream(ofIntSpliterator, intStream.isParallel())
.onClose(intStream::close)
.mapToDouble(subStream -> subStream.average().getAsDouble());
}
/**
* Generates a stream that is computed from a provided stream following two steps.
* The first steps maps this stream to an LongStream
that is then rolled following
* the same principle as the roll()
method. This steps builds a Stream<LongStream>
.
*
* Then the average()
method is called on each LongStream
using a mapper, and a
* DoubleStream
of averages is returned.
* The resulting stream has the same number of elements as the provided stream,
* minus the size of the window width, to preserve consistency of each collection.
* A NullPointerException
will be thrown if the provided stream or the mapper is null.
*
* @param stream the processed stream
* @param rollingFactor the size of the window to apply the collector on
* @param mapper the mapper applied
* @param the type of the provided stream
* @return a stream in which each value is the collection of the provided stream
*/
public static DoubleStream shiftingWindowAveragingLong(Stream stream, int rollingFactor, ToLongFunction super E> mapper) {
Objects.requireNonNull(stream);
Objects.requireNonNull(mapper);
LongStream longStream = stream.mapToLong(mapper);
return shiftingWindowAveragingLong(longStream, rollingFactor);
}
/**
* Generates a stream that is computed from a provided long stream by first rolling it in the same
* way as the roll()
method does. The average is then computed on each substream, to
* form the final double stream. No boxing / unboxing is conducted in the process.
*
The resulting stream has the same number of elements as the provided stream,
* minus the size of the window width, to preserve consistency of each collection.
* A NullPointerException
will be thrown if the provided stream is null.
*
* @param longStream the processed stream
* @param rollingFactor the size of the window to apply the collector on
* @return a stream in which each value is the collection of the provided stream
*/
public static DoubleStream shiftingWindowAveragingLong(LongStream longStream, int rollingFactor) {
Objects.requireNonNull(longStream);
RollingOfLongSpliterator ofLongSpliterator = RollingOfLongSpliterator.of(longStream.spliterator(), rollingFactor);
return StreamSupport.stream(ofLongSpliterator, longStream.isParallel())
.onClose(longStream::close)
.mapToDouble(subStream -> subStream.average().getAsDouble());
}
/**
* Generates a stream that is computed from a provided stream following two steps.
* The first steps maps this stream to an DoubleStream
that is then rolled following
* the same principle as the roll()
method. This steps builds a Stream<DoubleStream>
.
*
* Then the average()
method is called on each DoubleStream
using a mapper, and a
* DoubleStream
of averages is returned.
* The resulting stream has the same number of elements as the provided stream,
* minus the size of the window width, to preserve consistency of each collection.
* A NullPointerException
will be thrown if the provided stream or the mapper is null.
*
* @param stream the processed stream
* @param rollingFactor the size of the window to apply the collector on
* @param mapper the mapper applied
* @param the type of the provided stream
* @return a stream in which each value is the collection of the provided stream
*/
public static DoubleStream shiftingWindowAveragingDouble(Stream stream, int rollingFactor, ToDoubleFunction super E> mapper) {
Objects.requireNonNull(stream);
Objects.requireNonNull(mapper);
DoubleStream doubleStream = stream.mapToDouble(mapper);
return shiftingWindowAveragingDouble(doubleStream, rollingFactor);
}
/**
* Generates a stream that is computed from a provided double stream by first rolling it in the same
* way as the roll()
method does. The average is then computed on each substream, to
* form the final double stream. No boxing / unboxing is conducted in the process.
*
The resulting stream has the same number of elements as the provided stream,
* minus the size of the window width, to preserve consistency of each collection.
* A NullPointerException
will be thrown if the provided stream is null.
*
* @param doubleStream the processed stream
* @param rollingFactor the size of the window to apply the collector on
* @return a stream in which each value is the collection of the provided stream
*/
public static DoubleStream shiftingWindowAveragingDouble(DoubleStream doubleStream, int rollingFactor) {
Objects.requireNonNull(doubleStream);
RollingOfDoubleSpliterator ofDoubleSpliterator = RollingOfDoubleSpliterator.of(doubleStream.spliterator(), rollingFactor);
return StreamSupport.stream(ofDoubleSpliterator, doubleStream.isParallel())
.onClose(doubleStream::close)
.mapToDouble(subStream -> subStream.average().getAsDouble());
}
/**
* Generates a stream that is computed from a provided stream following two steps.
* The first steps maps this stream to an IntStream
that is then rolled following
* the same principle as the roll()
method. This steps builds a Stream<IntStream>
.
*
* Then int summary statistics are computed on each IntStream
using a collect()
call,
* and a Stream<IntSummaryStatistics>
is returned.
* The resulting stream has the same number of elements as the provided stream,
* minus the size of the window width, to preserve consistency of each collection.
* A NullPointerException
will be thrown if the provided stream or the mapper is null.
*
* @param stream the processed stream
* @param rollingFactor the size of the window to apply the collector on
* @param mapper the mapper applied
* @param the type of the provided stream
* @return a stream in which each value is the collection of the provided stream
*/
public static Stream shiftingWindowSummarizingInt(Stream stream, int rollingFactor, ToIntFunction super E> mapper) {
Objects.requireNonNull(stream);
Objects.requireNonNull(mapper);
IntStream intStream = stream.mapToInt(mapper);
return shiftingWindowSummarizingInt(intStream, rollingFactor);
}
/**
* Generates a stream that is computed from a provided int stream by first rolling it in the same
* way as the roll()
method does. Then a summarizing int operation is applied on each
* substream to form the final int summary stream. No boxing / unboxing is conducted in the process.
*
The resulting stream has the same number of elements as the provided stream,
* minus the size of the window width, to preserve consistency of each collection.
* A NullPointerException
will be thrown if the provided stream is null.
*
* @param intStream the processed stream
* @param rollingFactor the size of the window to apply the collector on
* @return a stream in which each value is the collection of the provided stream
*/
public static Stream shiftingWindowSummarizingInt(IntStream intStream, int rollingFactor) {
Objects.requireNonNull(intStream);
RollingOfIntSpliterator ofIntSpliterator = RollingOfIntSpliterator.of(intStream.spliterator(), rollingFactor);
return StreamSupport.stream(ofIntSpliterator, intStream.isParallel())
.onClose(intStream::close)
.map(
str -> str.collect(
IntSummaryStatistics::new,
IntSummaryStatistics::accept,
IntSummaryStatistics::combine
)
);
}
/**
* Generates a stream that is computed from a provided stream following two steps.
* The first steps maps this stream to an LongStream
that is then rolled following
* the same principle as the roll()
method. This steps builds a Stream<LongStream>
.
*
* Then long summary statistics are computed on each LongStream
using a collect()
call,
* and a Stream<LongSummaryStatistics>
is returned.
* The resulting stream has the same number of elements as the provided stream,
* minus the size of the window width, to preserve consistency of each collection.
* A NullPointerException
will be thrown if the provided stream or the mapper is null.
*
* @param stream the processed stream
* @param rollingFactor the size of the window to apply the collector on
* @param mapper the mapper applied
* @param the type of the provided stream
* @return a stream in which each value is the collection of the provided stream
*/
public static Stream shiftingWindowSummarizingLong(Stream stream, int rollingFactor, ToLongFunction super E> mapper) {
Objects.requireNonNull(stream);
Objects.requireNonNull(mapper);
LongStream longStream = stream.mapToLong(mapper);
return shiftingWindowSummarizingLong(longStream, rollingFactor);
}
/**
* Generates a stream that is computed from a provided long stream by first rolling it in the same
* way as the roll()
method does. Then a summarizing long operation is applied on each
* substream to form the final long summary stream. No boxing / unboxing is conducted in the process.
*
The resulting stream has the same number of elements as the provided stream,
* minus the size of the window width, to preserve consistency of each collection.
* A NullPointerException
will be thrown if the provided stream is null.
*
* @param longStream the processed stream
* @param rollingFactor the size of the window to apply the collector on
* @return a stream in which each value is the collection of the provided stream
*/
public static Stream shiftingWindowSummarizingLong(LongStream longStream, int rollingFactor) {
Objects.requireNonNull(longStream);
RollingOfLongSpliterator ofLongSpliterator = RollingOfLongSpliterator.of(longStream.spliterator(), rollingFactor);
return StreamSupport.stream(ofLongSpliterator, longStream.isParallel())
.onClose(longStream::close)
.map(
str -> str.collect(
LongSummaryStatistics::new,
(longSummaryStatistics, value) -> longSummaryStatistics.accept(value),
LongSummaryStatistics::combine
)
);
}
/**
* Generates a stream that is computed from a provided stream following two steps.
* The first steps maps this stream to a DoubleStream
that is then rolled following
* the same principle as the roll()
method. This steps builds a Stream<DoubleStream>
.
*
* Then double summary statistics are computed on each DoubleStream
using a collect()
call,
* and a Stream<DoubleSummaryStatistics>
is returned.
* The resulting stream has the same number of elements as the provided stream,
* minus the size of the window width, to preserve consistency of each collection.
* A NullPointerException
will be thrown if the provided stream is null.
*
* @param stream the processed stream
* @param rollingFactor the size of the window to apply the collector on
* @param mapper the mapper applied
* @param the type of the provided stream
* @return a stream in which each value is the collection of the provided stream
*/
public static Stream shiftingWindowSummarizingDouble(Stream stream, int rollingFactor, ToDoubleFunction super E> mapper) {
Objects.requireNonNull(stream);
Objects.requireNonNull(mapper);
DoubleStream doubleStream = stream.mapToDouble(mapper);
return shiftingWindowSummarizingLong(doubleStream, rollingFactor);
}
/**
* Generates a stream that is computed from a provided double stream by first rolling it in the same
* way as the roll()
method does. Then a summarizing double operation is applied on each
* substream to form the final double summary stream. No boxing / unboxing is conducted in the process.
*
The resulting stream has the same number of elements as the provided stream,
* minus the size of the window width, to preserve consistency of each collection.
* A NullPointerException
will be thrown if the provided stream is null.
*
* @param doubleStream the processed stream
* @param rollingFactor the size of the window to apply the collector on
* @return a stream in which each value is the collection of the provided stream
*/
public static Stream shiftingWindowSummarizingLong(DoubleStream doubleStream, int rollingFactor) {
Objects.requireNonNull(doubleStream);
RollingOfDoubleSpliterator ofDoubleSpliterator = RollingOfDoubleSpliterator.of(doubleStream.spliterator(), rollingFactor);
return StreamSupport.stream(ofDoubleSpliterator, doubleStream.isParallel())
.onClose(doubleStream::close)
.map(
str -> str.collect(
DoubleSummaryStatistics::new,
DoubleSummaryStatistics::accept,
DoubleSummaryStatistics::combine
)
);
}
/**
* Generates a stream of Map.Entry<E, E>
elements with all the cartesian product of the
* elements of the provided stream with itself.
* For a stream {a, b, c}
, a stream with the following elements is created:
* {(a, a), (a, b), (a, c), (b, a), (b, b), (b, c), (c, a), (c, b), (c, c)}
, where
* (a, b)
is the Map.Entry
with key a
and value b
.
* A NullPointerException
will be thrown if the provided stream is null.
*
* @param stream the processed stream
* @param the type of the provided stream
* @return a stream of the cartesian product
*/
public static Stream> crossProduct(Stream stream) {
Objects.requireNonNull(stream);
CrossProductOrderedSpliterator spliterator =
CrossProductOrderedSpliterator.of(stream.spliterator());
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
/**
* Generates a stream of Map.Entry<E, E>
elements with all the cartesian product of the
* elements of the provided stream with itself, without the entries in which the key and the
* value is equal.
* For a stream {a, b, c}
, a stream with the following elements is created:
* {(a, b), (a, c), (b, a), (b, c), (c, a), (c, b)}
, where
* (a, b)
is the Map.Entry
with key a
and value b
.
* A NullPointerException
will be thrown if the provided stream is null.
*
* @param stream the processed stream
* @param the type of the provided stream
* @return a stream of the cartesian product
*/
public static Stream> crossProductNoDoubles(Stream stream) {
Objects.requireNonNull(stream);
CrossProductOrderedSpliterator spliterator =
CrossProductOrderedSpliterator.noDoubles(stream.spliterator());
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
/**
* Generates a stream of Map.Entry<E, E>
elements with all the cartesian product of the
* elements of the provided stream with itself, in which the entries are such that the key is
* strictly lesser than the value, using the provided comparator.
* For a stream {a, b, c}
, a stream with the following elements is created:
* {(a, b), (a, c), (b, c)}
, where
* (a, b)
is the Map.Entry
with key a
and value b
.
* A NullPointerException
will be thrown if the provided stream or comparator is null.
*
* @param stream the processed stream
* @param comparator the comparator or the elements of the provided stream
* @param the type of the provided stream
* @return a stream of the cartesian product
*/
public static Stream> crossProductOrdered(Stream stream, Comparator comparator) {
Objects.requireNonNull(stream);
Objects.requireNonNull(comparator);
CrossProductOrderedSpliterator spliterator =
CrossProductOrderedSpliterator.ordered(stream.spliterator(), comparator);
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
/**
* Generates a stream of Map.Entry<E, E>
elements with all the cartesian product of the
* elements of the provided stream with itself, in which the entries are such that the key is
* strictly lesser than the value, using the natural order of E
.
* For a stream {a, b, c}
, a stream with the following elements is created:
* {(a, b), (a, c), (b, c)}
, where
* (a, b)
is the Map.Entry
with key a
and value b
.
* A NullPointerException
will be thrown if the provided stream is null.
*
* @param stream the processed stream
* @param the type of the provided stream
* @return a stream of the cartesian product
*/
public static > Stream> crossProductNaturallyOrdered(Stream stream) {
Objects.requireNonNull(stream);
CrossProductOrderedSpliterator spliterator =
CrossProductOrderedSpliterator.ordered(stream.spliterator(), Comparator.naturalOrder());
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
/**
* Generates a stream only composed of the greatest elements of the provided stream, compared using the provided
* comparator.
* A NullPointerException
will be thrown if the provided stream or the comparator is null.
*
* @param stream the processed stream
* @param comparator the comparator used to compare the elements of the stream
* @param the type of the provided stream
* @return a filtered stream
*/
public static Stream filteringAllMax(Stream stream, Comparator super E> comparator) {
Objects.requireNonNull(stream);
Objects.requireNonNull(comparator);
FilteringAllMaxSpliterator spliterator = FilteringAllMaxSpliterator.of(stream.spliterator(), comparator);
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
/**
* Generates a stream only composed of the greatest elements of the provided stream, compared using their
* natural order.
* A NullPointerException
will be thrown if the provided stream is null.
*
* @param stream the processed stream
* @param the type of the provided stream
* @return a filtered stream
*/
public static > Stream filteringAllMax(Stream stream) {
Objects.requireNonNull(stream);
return filteringAllMax(stream, Comparator.naturalOrder());
}
/**
* Generates a stream composed of the N greatest values of the provided stream, compared using the
* provided comparator. If there are no duplicates in the provided stream, then the returned stream will have
* N values, assuming that the input stream has more than N values.
* All the duplicates are copied in the returned stream, so in this case the number of elements in the
* returned stream may be greater than N. In this case, the number of different values is not guaranteed, and
* may be lesser than N.
* Since this operator extract maxes according to the provided comparator, the result is sorted from the
* greatest element to the smallest, thus in the decreasing order, according to the provided comparator.
* The provided implementation uses and insertion buffer of size N to keep the N maxes, as well as a hash
* map to keep the duplicates. This implementation becomes less and less efficient as N grows.
* A NullPointerException
will be thrown if the provided stream or the comparator is null.
* An IllegalArgumentException
is thrown if N is lesser than 1.
*
* @param stream the processed stream
* @param numberOfMaxes the number of different max values that should be returned. Note that the total number of
* values returned may be larger if there are duplicates in the stream
* @param comparator the comparator used to compare the elements of the stream
* @param the type of the provided stream
* @return the filtered stream
*/
public static Stream filteringMaxValues(Stream stream, int numberOfMaxes, Comparator super E> comparator) {
Objects.requireNonNull(stream);
Objects.requireNonNull(comparator);
FilteringMaxValuesSpliterator spliterator = FilteringMaxValuesSpliterator.of(stream.spliterator(), numberOfMaxes, comparator);
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
/**
* Generates a stream composed of the N greatest values of the provided stream, compared using the
* natural order. This method calls the filteringMaxValues()
with the natural order comparator,
* please refer to this javadoc for details. .
* A NullPointerException
will be thrown if the provided stream.
* An IllegalArgumentException
is thrown if N is lesser than 1.
*
* @param stream the processed stream
* @param numberOfMaxes the number of different max values that should be returned. Note that the total number of
* values returned may be larger if there are duplicates in the stream
* @param the type of the provided stream
* @return the filtered stream
*/
public static > Stream filteringMaxValues(Stream stream, int numberOfMaxes) {
Objects.requireNonNull(stream);
return filteringMaxValues(stream, numberOfMaxes, Comparator.naturalOrder());
}
/**
* Generates a stream composed of the N greatest different values of the provided stream, compared using the
* provided comparator. If there are no duplicates in the provided stream, then the returned stream will have
* N values, assuming that the input stream has more than N values.
* All the duplicates are removed in the returned stream, so in this case the number of elements in the
* returned stream may be lesser than N. In this case, the total number of values is not guaranteed, and
* may be lesser than N.
* Since this operator extract maxes according to the provided comparator, the result is sorted from the
* greatest element to the smallest, thus in the decreasing order, according to the provided comparator.
* The provided implementation uses and insertion buffer of size N to keep the N maxes.
* This implementation becomes less and less efficient as N grows.
* A NullPointerException
will be thrown if the provided stream or the comparator is null.
* An IllegalArgumentException
is thrown if N is lesser than 1.
*
* @param stream the processed stream
* @param numberOfMaxes the number of different max values that should be returned. Note that the total number of
* values returned may be larger if there are duplicates in the stream
* @param comparator the comparator used to compare the elements of the stream
* @param the type of the provided stream
* @return the filtered stream
*/
public static Stream filteringMaxKeys(Stream stream, int numberOfMaxes, Comparator super E> comparator) {
Objects.requireNonNull(stream);
Objects.requireNonNull(comparator);
FilteringMaxKeysSpliterator spliterator = FilteringMaxKeysSpliterator.of(stream.spliterator(), numberOfMaxes, comparator);
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
/**
* Generates a stream composed of the N greatest different values of the provided stream, compared using the
* natural order. This method calls the filteringMaxKeys()
with the natural order comparator,
* please refer to this javadoc for details.
* A NullPointerException
will be thrown if the provided stream is null.
* An IllegalArgumentException
is thrown if N is lesser than 1.
*
* @param stream the processed stream
* @param numberOfMaxes the number of different max values that should be returned. Note that the total number of
* values returned may be larger if there are duplicates in the stream
* @param the type of the provided stream
* @return the filtered stream
*/
public static > Stream filteringMaxKeys(Stream stream, int numberOfMaxes) {
Objects.requireNonNull(stream);
return filteringMaxKeys(stream, numberOfMaxes, Comparator.naturalOrder());
}
/**
* Generates a stream composed of the accumulation of its elements, through the use of the provided binary
* operator.
* For the stream {@code Stream.of(1, 1, 1, 1)}, and the {@code Integer::sum} operator,
* the following stream is returned: {@code Stream.of(1, 2, 3, 4)}
* For the stream {@code Stream.of(1, 2, 5, 3)}, and the {@code Integer::max} operator,
* the following stream is returned: {@code Stream.of(1, 2, 5, 5)}
* A NullPointerException
will be thrown if the provided stream or the operator is null.
* A IllegalArgumentException
will be thrown if the provided stream is not ordered.
*
* @param stream the processed stream
* @param operator the binary operator used to accumulate the elements of the stream
* @param the type of the provided stream
* @return the accumulated stream
*/
public static Stream accumulate(Stream stream, BinaryOperator operator) {
Objects.requireNonNull(stream);
Objects.requireNonNull(operator);
AccumulatingSpliterator spliterator = AccumulatingSpliterator.of(stream.spliterator(), operator);
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
/**
* Generates a stream composed of the accumulation of its elements, through the use of the provided binary
* operator.
* For the stream {@code Stream.of(1, 1, 1, 1)}, and the {@code Integer::sum} operator,
* the following stream is returned: {@code Stream.of(1, 2, 3, 4)}
* For the stream {@code Stream.of(1, 2, 5, 3)}, and the {@code Integer::max} operator,
* the following stream is returned: {@code Stream.of(1, 2, 5, 5)}
* A NullPointerException
will be thrown if the provided stream or the operator is null.
* A IllegalArgumentException
will be thrown if the provided stream is not ordered.
*
* @param stream the processed stream of entries
* @param operator the binary operator used to accumulate the values of the stream
* @param the type of the keys of the provided stream
* @param the type of the values provided stream
* @return the accumulated stream
*/
public static Stream> accumulateEntries(Stream> stream, BinaryOperator operator) {
Objects.requireNonNull(stream);
Objects.requireNonNull(operator);
AccumulatingEntriesSpliterator spliterator = AccumulatingEntriesSpliterator.of(stream.spliterator(), operator);
return StreamSupport.stream(spliterator, stream.isParallel()).onClose(stream::close);
}
}