All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.oliviercailloux.jaris.exceptions.CheckedStream Maven / Gradle / Ivy

package io.github.oliviercailloux.jaris.exceptions;

import com.google.common.collect.ImmutableList;
import io.github.oliviercailloux.jaris.throwing.TBiConsumer;
import io.github.oliviercailloux.jaris.throwing.TBiFunction;
import io.github.oliviercailloux.jaris.throwing.TBinaryOperator;
import io.github.oliviercailloux.jaris.throwing.TComparator;
import io.github.oliviercailloux.jaris.throwing.TConsumer;
import io.github.oliviercailloux.jaris.throwing.TFunction;
import io.github.oliviercailloux.jaris.throwing.TPredicate;
import io.github.oliviercailloux.jaris.throwing.TSupplier;
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. *

* *

* 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(TSupplier 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(TPredicate 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(TPredicate 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(TPredicate 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(TFunction, ? 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(TFunction 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(TComparator 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, TBinaryOperator 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.TBinaryOperator) * @see #min(Throwing.TComparator) * @see #max(Throwing.TComparator) * @see Stream#reduce(BinaryOperator) */ Optional reduce(TBinaryOperator 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.TBinaryOperator) * @see #reduce(Object, Throwing.TBinaryOperator) * @see Stream#reduce(Object, BiFunction, BinaryOperator) * {@code Stream.reduce(U, BiFunction, BinaryOperator)} */ U reduce(U identity, TBiFunction accumulator, TBinaryOperator 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.TBinaryOperator)}, {@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(TSupplier supplier, TBiConsumer accumulator, TBiConsumer 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.TSupplier, Throwing.TBiConsumer, Throwing.TBiConsumer)}, 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.TSupplier, Throwing.TBiConsumer, Throwing.TBiConsumer) * @see Collectors * @see Stream#collect(Collector) */ R collect(Collector 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(TPredicate 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(TPredicate 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(TPredicate 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(TConsumer 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(TConsumer 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.TConsumer) * @see Stream#forEachOrdered(Consumer) */ void forEachOrdered(TConsumer 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(TComparator 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(TComparator 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; }