blob: 267ec62b931d1b7e3c30cf0dd0e5877cec019887 [file]
/*
* Copyright (c) 2023, 2024, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.stream;
import jdk.internal.vm.annotation.ForceInline;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Supplier;
/**
* An intermediate operation that transforms a stream of input elements into a
* stream of output elements, optionally applying a final action when the end of
* the upstream is reached. The transformation may be stateless or stateful,
* and may buffer input before producing any output.
*
* <p>Gatherer operations can be performed either sequentially,
* or be parallelized -- if a combiner function is supplied.
*
* <p>There are many examples of gathering operations, including but not
* limited to:
* grouping elements into batches (windowing functions);
* de-duplicating consecutively similar elements; incremental accumulation
* functions (prefix scan); incremental reordering functions, etc. The class
* {@link java.util.stream.Gatherers} provides implementations of common
* gathering operations.
*
* @apiNote
* <p>A {@code Gatherer} is specified by four functions that work together to
* process input elements, optionally using intermediate state, and optionally
* perform a final action at the end of input. They are: <ul>
* <li>creating a new, potentially mutable, state ({@link #initializer()})</li>
* <li>integrating a new input element ({@link #integrator()})</li>
* <li>combining two states into one ({@link #combiner()})</li>
* <li>performing an optional final action ({@link #finisher()})</li>
* </ul>
*
* <p>Each invocation of {@link #initializer()}, {@link #integrator()},
* {@link #combiner()}, and {@link #finisher()} must return a semantically
* identical result.
*
* <p>Implementations of Gatherer must not capture, retain, or expose to
* other threads, the references to the state instance, or the downstream
* {@link Downstream} for longer than the invocation duration of the method
* which they are passed to.
*
* <p>Performing a gathering operation with a {@code Gatherer} should produce a
* result equivalent to:
*
* {@snippet lang = java:
* Gatherer.Downstream<? super R> downstream = ...;
* A state = gatherer.initializer().get();
* for (T t : data) {
* gatherer.integrator().integrate(state, t, downstream);
* }
* gatherer.finisher().accept(state, downstream);
* }
*
* <p>However, the library is free to partition the input, perform the
* integrations on the partitions, and then use the combiner function to
* combine the partial results to achieve a gathering operation. (Depending
* on the specific gathering operation, this may perform better or worse,
* depending on the relative cost of the integrator and combiner functions.)
*
* <p>In addition to the predefined implementations in {@link Gatherers}, the
* static factory methods {@code of(...)} and {@code ofSequential(...)}
* can be used to construct gatherers. For example, you could create a gatherer
* that implements the equivalent of
* {@link java.util.stream.Stream#map(java.util.function.Function)} with:
*
* {@snippet lang = java:
* public static <T, R> Gatherer<T, ?, R> map(Function<? super T, ? extends R> mapper) {
* return Gatherer.of(
* (unused, element, downstream) -> // integrator
* downstream.push(mapper.apply(element))
* );
* }
* }
*
* <p>Gatherers are designed to be <em>composed</em>; two or more Gatherers can
* be composed into a single Gatherer using the {@link #andThen(Gatherer)}
* method.
*
* {@snippet lang = java:
* // using the implementation of `map` as seen above
* Gatherer<Integer, ?, Integer> increment = map(i -> i + 1);
*
* Gatherer<Object, ?, String> toString = map(i -> i.toString());
*
* Gatherer<Integer, ?, String> incrementThenToString = increment.andThen(toString);
* }
*
* <p>As an example, a Gatherer implementing a sequential Prefix Scan could
* be done the following way:
*
* {@snippet lang = java:
* public static <T, R> Gatherer<T, ?, R> scan(
* Supplier<R> initial,
* BiFunction<? super R, ? super T, ? extends R> scanner) {
*
* class State {
* R current = initial.get();
* }
*
* return Gatherer.<T, State, R>ofSequential(
* State::new,
* Gatherer.Integrator.ofGreedy((state, element, downstream) -> {
* state.current = scanner.apply(state.current, element);
* return downstream.push(state.current);
* })
* );
* }
* }
*
* <p>Example of usage:
*
* {@snippet lang = java:
* // will contain: ["1", "12", "123", "1234", "12345", "123456", "1234567", "12345678", "123456789"]
* List<String> numberStrings =
* Stream.of(1,2,3,4,5,6,7,8,9)
* .gather(
* scan(() -> "", (string, number) -> string + number)
* )
* .toList();
* }
*
* @implSpec Libraries that implement transformations based on {@code Gatherer},
* such as {@link Stream#gather(Gatherer)}, must adhere to the following
* constraints:
* <ul>
* <li>Gatherers whose initializer is {@link #defaultInitializer()} are
* considered to be stateless, and invoking their initializer is optional.
* </li>
* <li>Gatherers whose integrator is an instance of {@link Integrator.Greedy}
* can be assumed not to short-circuit, and the return value of invoking
* {@link Integrator#integrate(Object, Object, Downstream)} does not need to
* be inspected.</li>
* <li>The first argument passed to the integration function, both
* arguments passed to the combiner function, and the argument passed to the
* finisher function must be the result of a previous invocation of the
* initializer or combiner functions.</li>
* <li>The implementation should not do anything with the result of any of
* the initializer or combiner functions other than to
* pass them again to the integrator, combiner, or finisher functions.</li>
* <li>Once a state object is passed to the combiner or finisher function,
* it is never passed to the integrator function again.</li>
* <li>When the integrator function returns {@code false},
* it shall be interpreted just as if there were no more elements to pass
* it.</li>
* <li>For parallel evaluation, the gathering implementation must manage
* that the input is properly partitioned, that partitions are processed
* in isolation, and combining happens only after integration is complete
* for both partitions.</li>
* <li>Gatherers whose combiner is {@link #defaultCombiner()} may only be
* evaluated sequentially. All other combiners allow the operation to be
* parallelized by initializing each partition in separation, invoking
* the integrator until it returns {@code false}, and then joining each
* partitions state using the combiner, and then invoking the finisher on
* the joined state. Outputs and state later in the input sequence will
* be discarded if processing an earlier partition short-circuits.</li>
* <li>Gatherers whose finisher is {@link #defaultFinisher()} are considered
* to not have an end-of-stream hook and invoking their finisher is
* optional.</li>
* </ul>
*
* @see Stream#gather(Gatherer)
* @see Gatherers
*
* @param <T> the type of input elements to the gatherer operation
* @param <A> the potentially mutable state type of the gatherer operation
* (often hidden as an implementation detail)
* @param <R> the type of output elements from the gatherer operation
* @since 24
*/
public interface Gatherer<T, A, R> {
/**
* A function that produces an instance of the intermediate state used for
* this gathering operation.
*
* @implSpec The implementation in this interface returns
* {@link #defaultInitializer()}.
*
* @return A function that produces an instance of the intermediate state
* used for this gathering operation
*/
default Supplier<A> initializer() {
return defaultInitializer();
};
/**
* A function which integrates provided elements, potentially using
* the provided intermediate state, optionally producing output to the
* provided {@link Downstream}.
*
* @return a function which integrates provided elements, potentially using
* the provided state, optionally producing output to the provided
* Downstream
*/
Integrator<A, T, R> integrator();
/**
* A function which accepts two intermediate states and combines them into
* one.
*
* @implSpec The implementation in this interface returns
* {@link #defaultCombiner()}.
*
* @return a function which accepts two intermediate states and combines
* them into one
*/
default BinaryOperator<A> combiner() {
return defaultCombiner();
}
/**
* A function which accepts the final intermediate state
* and a {@link Downstream} object, allowing to perform a final action at
* the end of input elements.
*
* @implSpec The implementation in this interface returns
* {@link #defaultFinisher()}.
*
* @return a function which transforms the intermediate result to the final
* result(s) which are then passed on to the provided Downstream
*/
default BiConsumer<A, Downstream<? super R>> finisher() {
return defaultFinisher();
}
/**
* Returns a composed Gatherer which connects the output of this Gatherer
* to the input of that Gatherer.
*
* @implSpec The implementation in this interface returns a new Gatherer
* which is semantically equivalent to the combination of
* {@code this} and {@code that} gatherer.
*
* @param that the other gatherer
* @param <RR> The type of output of that Gatherer
* @throws NullPointerException if the argument is {@code null}
* @return returns a composed Gatherer which connects the output of this
* Gatherer as input that Gatherer
*/
default <RR> Gatherer<T, ?, RR> andThen(Gatherer<? super R, ?, ? extends RR> that) {
Objects.requireNonNull(that);
return Gatherers.Composite.of(this, that);
}
/**
* Returns an initializer which is the default initializer of a Gatherer.
* The returned initializer identifies that the owner Gatherer is stateless.
*
* @implSpec This method always returns the same instance.
*
* @see Gatherer#initializer()
* @return the instance of the default initializer
* @param <A> the type of the state of the returned initializer
*/
static <A> Supplier<A> defaultInitializer() {
return Gatherers.Value.DEFAULT.initializer();
}
/**
* Returns a combiner which is the default combiner of a Gatherer.
* The returned combiner identifies that the owning Gatherer must only
* be evaluated sequentially.
*
* @implSpec This method always returns the same instance.
*
* @see Gatherer#combiner()
* @return the instance of the default combiner
* @param <A> the type of the state of the returned combiner
*/
static <A> BinaryOperator<A> defaultCombiner() {
return Gatherers.Value.DEFAULT.combiner();
}
/**
* Returns a {@code finisher} which is the default finisher of
* a {@code Gatherer}.
* The returned finisher identifies that the owning Gatherer performs
* no additional actions at the end of input.
*
* @implSpec This method always returns the same instance.
*
* @see Gatherer#finisher()
* @return the instance of the default finisher
* @param <A> the type of the state of the returned finisher
* @param <R> the type of the Downstream of the returned finisher
*/
static <A, R> BiConsumer<A, Downstream<? super R>> defaultFinisher() {
return Gatherers.Value.DEFAULT.finisher();
}
/**
* Returns a new, sequential, and stateless {@code Gatherer} described by
* the given {@code integrator}.
*
* @param integrator the integrator function for the new gatherer
* @param <T> the type of input elements for the new gatherer
* @param <R> the type of results for the new gatherer
* @throws NullPointerException if the argument is {@code null}
* @return the new {@code Gatherer}
*/
static <T, R> Gatherer<T, Void, R> ofSequential(
Integrator<Void, T, R> integrator) {
return of(
defaultInitializer(),
integrator,
defaultCombiner(),
defaultFinisher()
);
}
/**
* Returns a new, sequential, and stateless {@code Gatherer} described by
* the given {@code integrator} and {@code finisher}.
*
* @param integrator the integrator function for the new gatherer
* @param finisher the finisher function for the new gatherer
* @param <T> the type of input elements for the new gatherer
* @param <R> the type of results for the new gatherer
* @throws NullPointerException if any argument is {@code null}
* @return the new {@code Gatherer}
*/
static <T, R> Gatherer<T, Void, R> ofSequential(
Integrator<Void, T, R> integrator,
BiConsumer<Void, Downstream<? super R>> finisher) {
return of(
defaultInitializer(),
integrator,
defaultCombiner(),
finisher
);
}
/**
* Returns a new, sequential, {@code Gatherer} described by the given
* {@code initializer} and {@code integrator}.
*
* @param initializer the initializer function for the new gatherer
* @param integrator the integrator function for the new gatherer
* @param <T> the type of input elements for the new gatherer
* @param <A> the type of state for the new gatherer
* @param <R> the type of results for the new gatherer
* @throws NullPointerException if any argument is {@code null}
* @return the new {@code Gatherer}
*/
static <T, A, R> Gatherer<T, A, R> ofSequential(
Supplier<A> initializer,
Integrator<A, T, R> integrator) {
return of(
initializer,
integrator,
defaultCombiner(),
defaultFinisher()
);
}
/**
* Returns a new, sequential, {@code Gatherer} described by the given
* {@code initializer}, {@code integrator}, and {@code finisher}.
*
* @param initializer the initializer function for the new gatherer
* @param integrator the integrator function for the new gatherer
* @param finisher the finisher function for the new gatherer
* @param <T> the type of input elements for the new gatherer
* @param <A> the type of state for the new gatherer
* @param <R> the type of results for the new gatherer
* @throws NullPointerException if any argument is {@code null}
* @return the new {@code Gatherer}
*/
static <T, A, R> Gatherer<T, A, R> ofSequential(
Supplier<A> initializer,
Integrator<A, T, R> integrator,
BiConsumer<A, Downstream<? super R>> finisher) {
return of(
initializer,
integrator,
defaultCombiner(),
finisher
);
}
/**
* Returns a new, parallelizable, and stateless {@code Gatherer} described
* by the given {@code integrator}.
*
* @param integrator the integrator function for the new gatherer
* @param <T> the type of input elements for the new gatherer
* @param <R> the type of results for the new gatherer
* @throws NullPointerException if any argument is {@code null}
* @return the new {@code Gatherer}
*/
static <T, R> Gatherer<T, Void, R> of(Integrator<Void, T, R> integrator) {
return of(
defaultInitializer(),
integrator,
Gatherers.Value.DEFAULT.statelessCombiner,
defaultFinisher()
);
}
/**
* Returns a new, parallelizable, and stateless {@code Gatherer} described
* by the given {@code integrator} and {@code finisher}.
*
* @param integrator the integrator function for the new gatherer
* @param finisher the finisher function for the new gatherer
* @param <T> the type of input elements for the new gatherer
* @param <R> the type of results for the new gatherer
* @throws NullPointerException if any argument is {@code null}
* @return the new {@code Gatherer}
*/
static <T, R> Gatherer<T, Void, R> of(
Integrator<Void, T, R> integrator,
BiConsumer<Void, Downstream<? super R>> finisher) {
return of(
defaultInitializer(),
integrator,
Gatherers.Value.DEFAULT.statelessCombiner,
finisher
);
}
/**
* Returns a new, parallelizable, {@code Gatherer} described by the given
* {@code initializer}, {@code integrator}, {@code combiner} and
* {@code finisher}.
*
* @param initializer the initializer function for the new gatherer
* @param integrator the integrator function for the new gatherer
* @param combiner the combiner function for the new gatherer
* @param finisher the finisher function for the new gatherer
* @param <T> the type of input elements for the new gatherer
* @param <A> the type of state for the new gatherer
* @param <R> the type of results for the new gatherer
* @throws NullPointerException if any argument is {@code null}
* @return the new {@code Gatherer}
*/
static <T, A, R> Gatherer<T, A, R> of(
Supplier<A> initializer,
Integrator<A, T, R> integrator,
BinaryOperator<A> combiner,
BiConsumer<A, Downstream<? super R>> finisher) {
return new Gatherers.GathererImpl<>(
Objects.requireNonNull(initializer),
Objects.requireNonNull(integrator),
Objects.requireNonNull(combiner),
Objects.requireNonNull(finisher)
);
}
/**
* A Downstream object is the next stage in a pipeline of operations,
* to which elements can be sent.
* @param <T> the type of elements this downstream accepts
* @since 24
*/
@FunctionalInterface
interface Downstream<T> {
/**
* Pushes, if possible, the provided element downstream -- to the next
* stage in the pipeline.
*
* @implSpec If this method returns {@code false} then no further
* elements will be accepted and subsequent invocations of this method
* will return {@code false}.
*
* @param element the element to push downstream
* @return {@code true} if more elements can be sent,
* and {@code false} if not.
*/
boolean push(T element);
/**
* Checks whether the next stage is known to not want
* any more elements sent to it.
*
* @apiNote This is best-effort only, once this returns {@code true} it
* should never return {@code false} again for the same instance.
*
* @implSpec The implementation in this interface returns {@code false}.
*
* @return {@code true} if this Downstream is known not to want any
* more elements sent to it, {@code false} if otherwise
*/
default boolean isRejecting() { return false; }
}
/**
* An Integrator receives elements and processes them,
* optionally using the supplied state, and optionally sends incremental
* results downstream.
*
* @param <A> the type of state used by this integrator
* @param <T> the type of elements this integrator consumes
* @param <R> the type of results this integrator can produce
* @since 24
*/
@FunctionalInterface
interface Integrator<A, T, R> {
/**
* Performs an action given: the current state, the next element, and
* a downstream object; potentially inspecting and/or updating
* the state, optionally sending any number of elements downstream
* -- and then returns whether more elements are to be consumed or not.
*
* @param state The state to integrate into
* @param element The element to integrate
* @param downstream The downstream object of this integration
* @return {@code true} if subsequent integration is desired,
* {@code false} if not
*/
boolean integrate(A state, T element, Downstream<? super R> downstream);
/**
* Factory method for turning Integrator-shaped lambdas into
* Integrators.
*
* @param integrator a lambda to create as Integrator
* @return the given lambda as an Integrator
* @param <A> the type of state used by this integrator
* @param <T> the type of elements this integrator receives
* @param <R> the type of results this integrator can produce
*/
@ForceInline
static <A, T, R> Integrator<A, T, R> of(Integrator<A, T, R> integrator) {
return integrator;
}
/**
* Factory method for turning Integrator-shaped lambdas into
* {@link Greedy} Integrators.
*
* @param greedy a lambda to create as Integrator.Greedy
* @return the given lambda as a Greedy Integrator
* @param <A> the type of state used by this integrator
* @param <T> the type of elements this integrator receives
* @param <R> the type of results this integrator can produce
*/
@ForceInline
static <A, T, R> Greedy<A, T, R> ofGreedy(Greedy<A, T, R> greedy) {
return greedy;
}
/**
* Greedy Integrators consume all their input, and may only relay that
* the downstream does not want more elements.
*
* @implSpec This interface is used to communicate that no
* short-circuiting will be <i>initiated</i> by this Integrator, and that
* information can then be used to optimize evaluation.
*
* @param <A> the type of state used by this integrator
* @param <T> the type of elements this greedy integrator receives
* @param <R> the type of results this greedy integrator can produce
* @since 24
*/
@FunctionalInterface
interface Greedy<A, T, R> extends Integrator<A, T, R> { }
}
}