
io.github.oliviercailloux.jaris.exceptions.CheckedStream Maven / Gradle / Ivy
package io.github.oliviercailloux.jaris.exceptions;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* An equivalent to Java {@link Stream} which allows for functional interfaces that may throw
* checked exceptions; designed for people who do not like sneaky-throws.
*
* The following popular SO questions mention several libraries that deal with the “streams and
* checked exceptions” issue, but (at the time of writing) every library that I found there sneaky
* throw, apart from
* ThrowingStream.
*
*
* - Java 8: How do I work with exception
* throwing methods in streams?
* - Java 8: Lambda-Streams, Filter by
* Method with Exception
* - Aggregate runtime exceptions in Java 8
* streams
* - How can I throw CHECKED exceptions
* from inside Java 8 streams?
*
*
* This approach is heavily inspired by
* ThrowingStream; some differences
* are discussed here.
*
*
* The Javadoc for the methods that are also present in {@code Stream} (that is, most methods of
* this class) has been copied from the {@code Stream} analog, with minor modifications.
*
*
* @param the type of the stream elements
* @param an exception type that functionals used with this stream may throw, and that terminal
* operations on this stream may throw
* @see Stream
*/
public interface CheckedStream {
/**
* Returns a checked stream wrapping the given stream.
*
* The returned stream will behave as the delegate one except that it accepts functionals that
* declare checked exceptions. The returned stream throws, on terminal operations, any exception
* thrown by a functional operation during the stream operations.
*
*
* @param the type of the stream elements
* @param an exception type that functionals used with the returned stream may throw, and
* therefore, that terminal operations on the returned stream may throw
* @param delegate the stream executing operations
* @return a checked stream delegating to the given stream
*/
public static CheckedStream wrapping(Stream delegate) {
return CheckedStreamImpl.wrapping(delegate);
}
/**
* Returns a checked stream wrapping the stream produced by {@code collection.stream()}.
*
* The returned stream will behave as the delegate one except that it accepts functionals that
* declare checked exceptions. The returned stream throws, on terminal operations, any exception
* thrown by a functional operation during the stream operations.
*
*
* @param the type of the stream elements
* @param an exception type that functionals used with the returned stream may throw, and
* therefore, that terminal operations on the returned stream may throw
* @param collection the source
* @return a checked stream using the given collection as source
*/
public static CheckedStream from(Collection collection) {
return wrapping(collection.stream());
}
/**
* Returns an infinite sequential unordered checked stream where each element is generated by the
* provided {@code Throwing.Supplier}. This is suitable for generating constant streams, streams
* of random elements, etc.
*
* The returned stream accepts functionals that declare checked exceptions. The returned stream
* throws, on terminal operations, any exception thrown by a functional operation during the
* stream operations (including by the given generator).
*
*
* @param the type of stream elements
* @param an exception type that functionals used with the returned stream may throw, and
* therefore, that terminal operations on the returned stream may throw
* @param generator the {@code Throwing.Supplier} of generated elements
* @return a new infinite sequential unordered {@code CheckedStream}
* @see Stream#generate(Supplier)
*/
public static CheckedStream
generate(Throwing.Supplier extends T, ? extends X> generator) {
return CheckedStreamImpl.generate(generator);
}
/**
* Returns a checked stream consisting of the distinct elements (according to
* {@link Object#equals(Object)}) of this stream.
*
*
* For ordered streams, the selection of distinct elements is stable (for duplicated elements, the
* element appearing first in the encounter order is preserved.) For unordered streams, no
* stability guarantees are made.
*
*
* This is a stateful intermediate operation.
*
* @return the new stream
* @see Stream#distinct()
*/
CheckedStream distinct();
/**
* Returns, if this stream is ordered, a checked stream consisting of the remaining elements of
* this stream after dropping the longest prefix of elements that match the given predicate.
* Otherwise returns, if this stream is unordered, a stream consisting of the remaining elements
* of this stream after dropping a subset of elements that match the given predicate.
*
*
* If this stream is ordered then the longest prefix is a contiguous sequence of elements of this
* stream that match the given predicate. The first element of the sequence is the first element
* of this stream, and the element immediately following the last element of the sequence does not
* match the given predicate.
*
*
* If this stream is unordered, and some (but not all) elements of this stream match the given
* predicate, then the behavior of this operation is nondeterministic; it is free to drop any
* subset of matching elements (which includes the empty set).
*
*
* Independent of whether this stream is ordered or unordered if all elements of this stream match
* the given predicate then this operation drops all elements (the result is an empty stream), or
* if no elements of the stream match the given predicate then no elements are dropped (the result
* is the same as the input).
*
*
* This is a stateful intermediate operation.
*
* @param predicate a non-interfering,
* stateless predicate to apply to
* elements to determine the longest prefix of elements.
* @return the new stream
* @see Stream#dropWhile(Predicate)
*/
CheckedStream dropWhile(Throwing.Predicate super T, ? extends X> predicate);
/**
* Returns, if this stream is ordered, a checked stream consisting of the longest prefix of
* elements taken from this stream that match the given predicate. Otherwise returns, if this
* stream is unordered, a stream consisting of a subset of elements taken from this stream that
* match the given predicate.
*
*
* If this stream is ordered then the longest prefix is a contiguous sequence of elements of this
* stream that match the given predicate. The first element of the sequence is the first element
* of this stream, and the element immediately following the last element of the sequence does not
* match the given predicate.
*
*
* If this stream is unordered, and some (but not all) elements of this stream match the given
* predicate, then the behavior of this operation is nondeterministic; it is free to take any
* subset of matching elements (which includes the empty set).
*
*
* Independent of whether this stream is ordered or unordered if all elements of this stream match
* the given predicate then this operation takes all elements (the result is the same as the
* input), or if no elements of the stream match the given predicate then no elements are taken
* (the result is an empty stream).
*
*
* This is a short-circuiting stateful intermediate
* operation.
*
* @param predicate a non-interfering,
* stateless predicate to apply to
* elements to determine the longest prefix of elements.
* @return the new stream
* @see Stream#takeWhile(Predicate)
*/
CheckedStream takeWhile(Throwing.Predicate super T, ? extends X> predicate);
/**
* Returns a checked stream consisting of the elements of this stream that match the given
* predicate.
*
*
* This is an intermediate operation.
*
* @param predicate a non-interfering,
* stateless predicate to apply to each
* element to determine if it should be included
* @return the new stream
* @see Stream#filter(Predicate)
*/
CheckedStream filter(Throwing.Predicate super T, ? extends X> predicate);
/**
* Returns a checked stream consisting of the results of replacing each element of this stream
* with the contents of a mapped stream produced by applying the provided mapping function to each
* element. Each mapped stream is {@link java.util.stream.BaseStream#close() closed} after its
* contents have been placed into this stream. (If a mapped stream is {@code null} an empty stream
* is used, instead.)
*
*
* This is an intermediate operation.
*
* @param The element type of the new stream
* @param mapper a non-interfering,
* stateless function to apply to each
* element which produces a stream of new values
* @return the new stream
* @see Stream#flatMap(Function)
*/
CheckedStream
flatMap(Throwing.Function super T, ? extends Stream extends R>, ? extends X> mapper);
/**
* Returns a checked stream consisting of the elements of this stream, truncated to be no longer
* than {@code maxSize} in length.
*
*
* This is a short-circuiting stateful intermediate
* operation.
*
* @param maxSize the number of elements the stream should be limited to
* @return the new stream
* @throws IllegalArgumentException if {@code maxSize} is negative
* @see Stream#limit(long)
*/
CheckedStream limit(long maxSize);
/**
* Returns a checked stream consisting of the results of applying the given function to the
* elements of this stream.
*
*
* This is an intermediate operation.
*
* @param The element type of the new stream
* @param mapper a non-interfering,
* stateless function to apply to each
* element
* @return the new stream
* @see Stream#map(Function)
*/
CheckedStream map(Throwing.Function super T, ? extends R, ? extends X> mapper);
/**
* Returns a checked stream consisting of the remaining elements of this stream after discarding
* the first {@code n} elements of the stream. If this stream contains fewer than {@code n}
* elements then an empty stream will be returned.
*
*
* This is a stateful intermediate operation.
*
* @param n the number of leading elements to skip
* @return the new stream
* @throws IllegalArgumentException if {@code n} is negative
* @see Stream#skip(long)
*/
CheckedStream skip(long n);
/**
* Returns a checked stream consisting of the elements of this stream, sorted according to natural
* order. If the elements of this stream are not {@code Comparable}, a
* {@code java.lang.ClassCastException} may be thrown when the terminal operation is executed.
*
*
* For ordered streams, the sort is stable. For unordered streams, no stability guarantees are
* made.
*
*
* This is a stateful intermediate operation.
*
* @return the new stream
* @see Stream#sorted()
*/
CheckedStream sorted();
/**
* Returns a checked stream consisting of the elements of this stream, sorted according to the
* provided {@code Comparator}.
*
*
* For ordered streams, the sort is stable. For unordered streams, no stability guarantees are
* made.
*
*
* This is a stateful intermediate operation.
*
* @param comparator a non-interfering,
* stateless {@code Comparator} to be used
* to compare stream elements
* @return the new stream
* @see Stream#sorted(Comparator)
*/
CheckedStream sorted(Throwing.Comparator super T, ? extends X> comparator);
/**
* Performs a reduction on the elements of this
* stream, using the provided identity value and an
* associative accumulation function, and returns
* the reduced value. This is equivalent to:
*
*
* {@code
* T result = identity;
* for (T element : this stream)
* result = accumulator.apply(result, element)
* return result;
* }
*
*
* but is not constrained to execute sequentially.
*
*
* The {@code identity} value must be an identity for the accumulator function. This means that
* for all {@code t}, {@code accumulator.apply(identity, t)} is equal to {@code t}. The
* {@code accumulator} function must be an
* associative function.
*
*
* This is a terminal operation.
*
* @param identity the identity value for the accumulating function
* @param accumulator an associative,
* non-interfering,
* stateless function for combining two
* values
* @return the result of the reduction
* @throws X if any functional interface operating on this stream throws a checked exception
* @see Stream#reduce(Object, BinaryOperator) Stream.reduce(T, BinaryOperator)
*/
T reduce(T identity, Throwing.BinaryOperator accumulator) throws X;
/**
* Performs a reduction on the elements of this
* stream, using an associative accumulation
* function, and returns an {@code Optional} describing the reduced value, if any. This is
* equivalent to:
*
*
* {@code
* boolean foundAny = false;
* T result = null;
* for (T element : this stream) {
* if (!foundAny) {
* foundAny = true;
* result = element;
* }
* else
* result = accumulator.apply(result, element);
* }
* return foundAny ? Optional.of(result) : Optional.empty();
* }
*
*
* but is not constrained to execute sequentially.
*
*
* The {@code accumulator} function must be an
* associative function.
*
*
* This is a terminal operation.
*
* @param accumulator an associative,
* non-interfering,
* stateless function for combining two
* values
* @return an {@link Optional} describing the result of the reduction
* @throws NullPointerException if the result of the reduction is null
* @throws X if any functional interface operating on this stream throws a checked exception
* @see #reduce(Object, Throwing.BinaryOperator)
* @see #min(Throwing.Comparator)
* @see #max(Throwing.Comparator)
* @see Stream#reduce(BinaryOperator)
*/
Optional reduce(Throwing.BinaryOperator accumulator) throws X;
/**
* Performs a reduction on the elements of this
* stream, using the provided identity, accumulation and combining functions. This is equivalent
* to:
*
*
* {@code
* U result = identity;
* for (T element : this stream)
* result = accumulator.apply(result, element)
* return result;
* }
*
*
* but is not constrained to execute sequentially.
*
*
* The {@code identity} value must be an identity for the combiner function. This means that for
* all {@code u}, {@code combiner(identity, u)} is equal to {@code u}. Additionally, the
* {@code combiner} function must be compatible with the {@code accumulator} function; for all
* {@code u} and {@code t}, the following must hold:
*
*
* {@code
* combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)
* }
*
*
*
* This is a terminal operation.
*
* @param The type of the result
* @param identity the identity value for the combiner function
* @param accumulator an associative,
* non-interfering,
* stateless function for incorporating an
* additional element into a result
* @param combiner an associative,
* non-interfering,
* stateless function for combining two
* values, which must be compatible with the accumulator function
* @return the result of the reduction
* @throws X if any functional interface operating on this stream throws a checked exception
* @see #reduce(Throwing.BinaryOperator)
* @see #reduce(Object, Throwing.BinaryOperator)
* @see Stream#reduce(Object, BiFunction, BinaryOperator)
* {@code Stream.reduce(U, BiFunction, BinaryOperator)}
*/
U reduce(U identity, Throwing.BiFunction accumulator,
Throwing.BinaryOperator combiner) throws X;
/**
* Performs a mutable reduction operation on
* the elements of this stream. A mutable reduction is one in which the reduced value is a mutable
* result container, such as an {@code ArrayList}, and elements are incorporated by updating the
* state of the result rather than by replacing the result. This produces a result equivalent to:
*
*
* {@code
* R result = supplier.get();
* for (T element : this stream)
* accumulator.accept(result, element);
* return result;
* }
*
*
*
* Like {@link #reduce(Object, Throwing.BinaryOperator)}, {@code collect} operations can be
* parallelized without requiring additional synchronization.
*
*
* This is a terminal operation.
*
* @param the type of the mutable result container
* @param supplier a function that creates a new mutable result container. For a parallel
* execution, this function may be called multiple times and must return a fresh value each
* time.
* @param accumulator an associative,
* non-interfering,
* stateless function that must fold an
* element into a result container.
* @param combiner an associative,
* non-interfering,
* stateless function that accepts two
* partial result containers and merges them, which must be compatible with the accumulator
* function. The combiner function must fold the elements from the second result container
* into the first result container.
* @return the result of the reduction
* @throws X if any functional interface operating on this stream throws a checked exception
* @see Stream#collect(Supplier, BiConsumer, BiConsumer)
*/
R collect(Throwing.Supplier supplier,
Throwing.BiConsumer accumulator,
Throwing.BiConsumer combiner) throws X;
/**
* Performs a mutable reduction operation on
* the elements of this stream using a {@code Collector}. A {@code Collector} encapsulates the
* functions used as arguments to
* {@link #collect(Throwing.Supplier, Throwing.BiConsumer, Throwing.BiConsumer)}, allowing for
* reuse of collection strategies and composition of collect operations such as multiple-level
* grouping or partitioning.
*
*
* If the stream is parallel, and the {@code Collector} is
* {@link Collector.Characteristics#CONCURRENT concurrent}, and either the stream is unordered or
* the collector is {@link Collector.Characteristics#UNORDERED unordered}, then a concurrent
* reduction will be performed (see {@link Collector} for details on concurrent reduction.)
*
*
* This is a terminal operation.
*
*
* When executed in parallel, multiple intermediate results may be instantiated, populated, and
* merged so as to maintain isolation of mutable data structures. Therefore, even when executed in
* parallel with non-thread-safe data structures (such as {@code ArrayList}), no additional
* synchronization is needed for a parallel reduction.
*
* @param the type of the result
* @param the intermediate accumulation type of the {@code Collector}
* @param collector the {@code Collector} describing the reduction
* @return the result of the reduction
* @throws X if any functional interface operating on this stream throws a checked exception
* @see #collect(Throwing.Supplier, Throwing.BiConsumer, Throwing.BiConsumer)
* @see Collectors
* @see Stream#collect(Collector)
*/
R collect(Collector super T, A, R> collector) throws X;
/**
* Returns whether all elements of this stream match the provided predicate. May not evaluate the
* predicate on all elements if not necessary for determining the result. If the stream is empty
* then {@code true} is returned and the predicate is not evaluated.
*
*
* This is a short-circuiting terminal operation.
*
* @param predicate a non-interfering,
* stateless predicate to apply to
* elements of this stream
* @return {@code true} if either all elements of the stream match the provided predicate or the
* stream is empty, otherwise {@code false}
* @throws X if any functional interface operating on this stream throws a checked exception
* @see Stream#allMatch(Predicate)
*/
boolean allMatch(Throwing.Predicate super T, ? extends X> predicate) throws X;
/**
* Returns whether any elements of this stream match the provided predicate. May not evaluate the
* predicate on all elements if not necessary for determining the result. If the stream is empty
* then {@code false} is returned and the predicate is not evaluated.
*
*
* This is a short-circuiting terminal operation.
*
* @param predicate a non-interfering,
* stateless predicate to apply to
* elements of this stream
* @return {@code true} if any elements of the stream match the provided predicate, otherwise
* {@code false}
* @throws X if any functional interface operating on this stream throws a checked exception
* @see Stream#anyMatch(Predicate)
*/
boolean anyMatch(Throwing.Predicate super T, ? extends X> predicate) throws X;
/**
* Returns whether no elements of this stream match the provided predicate. May not evaluate the
* predicate on all elements if not necessary for determining the result. If the stream is empty
* then {@code true} is returned and the predicate is not evaluated.
*
*
* This is a short-circuiting terminal operation.
*
* @param predicate a non-interfering,
* stateless predicate to apply to
* elements of this stream
* @return {@code true} if either no elements of the stream match the provided predicate or the
* stream is empty, otherwise {@code false}
* @throws X if any functional interface operating on this stream throws a checked exception
* @see Stream#noneMatch(Predicate)
*/
boolean noneMatch(Throwing.Predicate super T, ? extends X> predicate) throws X;
/**
* Returns a stream consisting of the elements of this stream, additionally performing the
* provided action on each element as elements are consumed from the resulting stream.
*
*
* This is an intermediate operation.
*
*
* For parallel stream pipelines, the action may be called at whatever time and in whatever thread
* the element is made available by the upstream operation. If the action modifies shared state,
* it is responsible for providing the required synchronization.
*
* @param action a non-interfering action to
* perform on the elements as they are consumed from the stream
* @return the new stream
* @throws X if any functional interface operating on this stream throws a checked exception
* @see Stream#peek(Consumer)
*/
CheckedStream peek(Throwing.Consumer super T, ? extends X> action) throws X;
/**
* Returns the count of elements in this stream. This is a special case of a
* reduction and is equivalent to:
*
*
* {@code
* return mapToLong(e -> 1L).sum();
* }
*
*
*
* This is a terminal operation.
*
* @return the count of elements in this stream
* @throws X if any functional interface operating on this stream throws a checked exception
* @see Stream#count()
*/
long count() throws X;
/**
* Returns an {@link Optional} describing some element of the stream, or an empty {@code Optional}
* if the stream is empty.
*
*
* This is a short-circuiting terminal operation.
*
*
* The behavior of this operation is explicitly nondeterministic; it is free to select any element
* in the stream. This is to allow for maximal performance in parallel operations; the cost is
* that multiple invocations on the same source may not return the same result. (If a stable
* result is desired, use {@link #findFirst()} instead.)
*
* @return an {@code Optional} describing some element of this stream, or an empty
* {@code Optional} if the stream is empty
* @throws NullPointerException if the element selected is null
* @throws X if any functional interface operating on this stream throws a checked exception
* @see #findFirst()
* @see Stream#findAny()
*/
Optional findAny() throws X;
/**
* Returns an {@link Optional} describing the first element of this stream, or an empty
* {@code Optional} if the stream is empty. If the stream has no encounter order, then any element
* may be returned.
*
*
* This is a short-circuiting terminal operation.
*
* @return an {@code Optional} describing the first element of this stream, or an empty
* {@code Optional} if the stream is empty
* @throws NullPointerException if the element selected is null
* @throws X if any functional interface operating on this stream throws a checked exception
* @see Stream#findFirst()
*/
Optional findFirst() throws X;
/**
* Performs an action for each element of this stream.
*
*
* This is a terminal operation.
*
*
* The behavior of this operation is explicitly nondeterministic. For parallel stream pipelines,
* this operation does not guarantee to respect the encounter order of the stream, as
* doing so would sacrifice the benefit of parallelism. For any given element, the action may be
* performed at whatever time and in whatever thread the library chooses. If the action accesses
* shared state, it is responsible for providing the required synchronization.
*
* @param action a non-interfering action to
* perform on the elements
* @throws X if any functional interface operating on this stream throws a checked exception
* @see Stream#forEach(Consumer)
*/
void forEach(Throwing.Consumer super T, ? extends X> action) throws X;
/**
* Performs an action for each element of this stream, in the encounter order of the stream if the
* stream has a defined encounter order.
*
*
* This is a terminal operation.
*
*
* This operation processes the elements one at a time, in encounter order if one exists.
* Performing the action for one element
* happens-before
* performing the action for subsequent elements, but for any given element, the action may be
* performed in whatever thread the library chooses.
*
* @param action a non-interfering action to
* perform on the elements
* @throws X if any functional interface operating on this stream throws a checked exception
* @see #forEach(Throwing.Consumer)
* @see Stream#forEachOrdered(Consumer)
*/
void forEachOrdered(Throwing.Consumer super T, ? extends X> action) throws X;
/**
* Returns the maximum element of this stream according to the provided {@code Comparator}. This
* is a special case of a reduction.
*
*
* This is a terminal operation.
*
* @param comparator a non-interfering,
* stateless {@code Comparator} to compare
* elements of this stream
* @return an {@code Optional} describing the maximum element of this stream, or an empty
* {@code Optional} if the stream is empty
* @throws NullPointerException if the maximum element is null
* @throws X if any functional interface operating on this stream throws a checked exception
* @see Stream#max(Comparator)
*/
Optional max(Throwing.Comparator super T, ? extends X> comparator) throws X;
/**
* Returns the minimum element of this stream according to the provided {@code Comparator}. This
* is a special case of a reduction.
*
*
* This is a terminal operation.
*
* TODO think about returning a CheckedOptional, or TryOptional, that allows for an extended Map
* operation.
*
* @param comparator a non-interfering,
* stateless {@code Comparator} to compare
* elements of this stream
* @return an {@code Optional} describing the minimum element of this stream, or an empty
* {@code Optional} if the stream is empty
* @throws NullPointerException if the minimum element is null
* @throws X if any functional interface operating on this stream throws a checked exception
* @see Stream#min(Comparator)
*/
Optional min(Throwing.Comparator super T, ? extends X> comparator) throws X;
/**
* Accumulates the elements of this stream into an immutable list.
*
* @return an immutable list
* @throws X if any functional interface operating on this stream throws a checked exception
*/
ImmutableList toList() throws X;
}