Java tutorial
/* * The MIT License * * Copyright 2015 Marius Filip. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package enumj; import java.lang.reflect.Array; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.InputMismatchException; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.Spliterator; import java.util.Spliterators; import java.util.function.BiFunction; import java.util.function.BinaryOperator; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.IntSupplier; import java.util.function.IntUnaryOperator; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.function.UnaryOperator; import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; import org.apache.commons.lang3.mutable.Mutable; import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.commons.lang3.mutable.MutableObject; import org.apache.commons.lang3.tuple.Pair; /** * {@code Iterator} with high composability. * <p> * Enumerators are {@link Iterator} objects with the following characteristics: * </p> * <ul> * <li><em>High composability</em>: some operations can be composed * iteratively</li> * <li><em>Shareability</em>: shareable enumerators may participate in * multiple pipelines, processed independently</li> * <li><em>Fault tolerance</em>: fault-tolerant enumerators allow for * exceptions in the middle of the pipeline without stopping the whole * computation * <li><em>Lazy evaluations</em>: enumerators allow for lazy evaluation in * both returned values and input sequences * <li><em>Choice composition and zipping</em>: construction of enumerators * from multiple sub-sequences by choosing or zipping * <li><em>Compatibility and fluent syntax</em>: enumerators support fluent * syntax like {@link Stream} classes and are compatible with most major * collection and sequence constructions: {@link Iterator}, {@link Iterable}, * {@link Enumeration}, {@link Stream}, {@link Spliterator} and {@link Supplier} * of elements. * </ul> * <p> * <strong>High composability</strong> * </p> * <p> * The following operations can be composed iteratively many times and the * resulting enumerator will not overflow the stack when enumerating: * </p> * <ul> * <li>{@link #as(java.lang.Class)}</li> * <li>{@link #asFiltered(java.lang.Class)}</li> * <li>{@link #asOptional()}</li> * <li>{@link #append(java.lang.Object...)}</li> * <li>{@link #concatOn(java.lang.Object...)}</li> * <li>{@link #concat(java.util.Enumeration)}</li> * <li>{@link #concat(java.lang.Iterable)}</li> * <li>{@link #concat(java.util.Iterator)}</li> * <li>{@link #concat(java.util.Spliterator)}</li> * <li>{@link #concat(java.util.stream.Stream)}</li> * <li>{@link #concat(java.util.function.Supplier)}</li> * <li>{@link #filter(java.util.function.Predicate)}</li> * <li>{@link #flatMap(java.util.function.Function)}</li> * <li>{@link #limit(long)}</li> * <li>{@link #limitWhile(java.util.function.Predicate)}</li> * <li>{@link #map(java.util.function.Function)}</li> * <li>{@link #map(java.util.function.BiFunction)}</li> * <li>{@link #peek(java.util.function.Consumer)}</li> * <li>{@link #prependOn(java.lang.Object...)}</li> * <li>{@link #prepend(java.util.Enumeration)}</li> * <li>{@link #prepend(java.lang.Iterable)}</li> * <li>{@link #prepend(java.util.Iterator)}</li> * <li>{@link #prepend(java.util.Spliterator)}</li> * <li>{@link #prepend(java.util.stream.Stream)}</li> * <li>{@link #prepend(java.util.function.Supplier)}</li> * <li>{@link #repeatEach(int)}</li> * <li>{@link #skip(long)}</li> * <li>{@link #skipWhile(java.util.function.Predicate)}</li> * <li>{@link #take(long)}</li> * <li>{@link #takeWhile(java.util.function.Predicate)}</li> * <li>{@link #zipAll(java.util.Iterator, java.util.Iterator...)}</li> * <li>{@link #zipAny(java.util.Iterator)}</li> * <li>{@link #zipBoth(java.util.Iterator)}</li> * <li>{@link #zipLeft(java.util.Iterator)}</li> * <li>{@link #zipRight(java.util.Iterator)}</li> * </ul> * <p> * <strong>Shareability</strong> * </p> * <p> * {@link ShareableEnumerator} can spawn instances * of {@link SharingEnumerator} which in turn can share the same sequence * without traversing it more than once. {@link #asShareable()} converts any * enumerator into a {@link ShareableEnumerator}. * </p> * <p> * <strong>Fault tolerance</strong> * </p> * <p> * Fault-tolerant enumerators accept an error handler which * is being called whenever the tryPipelineOut of enumerating throws an * exception. The error handler consumes the error and the pipeline doesn't * stop. {@link #asTolerant(java.util.function.Consumer)} converts any * enumerator into a fault-tolerant enumerator. * </p> * <p> * <strong>Lazy evaluations</strong> * </p> * <p> * The following calls allow lazy specification of input sequences: * </p> * <ul> * <li>{@link #ofLazyEnumeration(java.util.function.Supplier)}</li> * <li>{@link #ofLazyIterable(java.util.function.Supplier)}</li> * <li>{@link #ofLazyIterator(java.util.function.Supplier)}</li> * <li>{@link #ofLazySpliterator(java.util.function.Supplier)}</li> * <li>{@link #ofLazyStream(java.util.function.Supplier)}</li> * </ul> * <p> * <strong>Choice composition and zipping</strong> * </p> * <p> * Enumerators can be constructed of sub-sequences by choosing elements * from them: * </p> * <ul> * <li>{@link #choiceOf(java.util.function.IntSupplier, * java.util.Iterator, * java.util.Iterator, * java.util.Iterator...)}</li> * <li>{@link #choiceOf(java.util.function.IntSupplier, * java.util.function.IntUnaryOperator, * java.util.Iterator, * java.util.Iterator, * java.util.Iterator...)}</li> * </ul> * <p> * Enumerators also have four zipping operations: * </p> * <ul> * <li>{@link #zipAny(java.util.Iterator)}</li> * <li>{@link #zipBoth(java.util.Iterator)}</li> * <li>{@link #zipLeft(java.util.Iterator)}</li> * <li>{@link #zipRight(java.util.Iterator)}</li> * </ul> * @param <E> type of enumerated elements. */ public interface Enumerator<E> extends Iterator<E> { /** * Returns whether the enumerator has started enumerating. * <p> * This method returns false before the first call * to {@link hasNext} and true afterwards. * </p> * * @return true if the enumerator has started enumerating, false otherwise. * @see #hasNext() * @see #next() */ public boolean enumerating(); /** * Returns an enumerator returning the given elements. * * @param <E> the type of elements being enumerated. * @param elements the elements returned by the enumerator. * @return the new {@link Enumerator}. */ @SafeVarargs public static <E> Enumerator<E> on(E... elements) { return new ArrayEnumerator(elements); } /** * Returns an enumerator iterating over the given array. * * @param <E> type of enumerated elements. * @param elements array to iterate upon. * @return the new {@link Enumerator}. */ public static <E> Enumerator<E> of(E[] elements) { return new ArrayEnumerator(elements); } /** * Returns an enumerator enumerating over an existing * {@code Iterator}. * <p> * If {@code source} is of type {@link Enumerator} then this method * returns {@code source}. * </p> * * @param <E> the type of elements being enumerated. * @param source the {@link Iterator} being enumerated upon. * @return the new enumerator. * @exception IllegalArgumentException {@code source} is null. */ public static <E> Enumerator<E> of(Iterator<E> source) { return IteratorEnumerator.of(source); } /** * Returns an enumerator enumerating over the elements of an * existing {@code Iterable}. * * @param <E> the type of elements being enumerated. * @param source the {@link Iterable} being enumerated upon. * @return the new enumerator. * @exception IllegalArgumentException {@code source} is null. */ public static <E> Enumerator<E> of(Iterable<E> source) { return of(source.iterator()); } /** * Returns an enumerator enumerating over the elements of an * existing {@code Enumeration}. * * @param <E> the type of elements being enumerated. * @param source the {@link Enumeration} being enumerated upon * @return the new enumerator. * @exception IllegalArgumentException {@code source} is null. */ public static <E> Enumerator<E> of(Enumeration<E> source) { return new EnumerationEnumerator(source); } /** * Returns an enumerator enumerating over the elements of an existing * {@code Stream}. * <p> * If {@link Stream#isParallel()} returns true on * {@code source} then the order of elements is undetermined. * </p> * * @param <E> the type of elements being enumerated. * @param source the {@link Stream} being enumerated upon. * @return the new enumerator. * @exception IllegalArgumentException {@code source} is null. */ public static <E> Enumerator<E> of(Stream<E> source) { Checks.ensureNotNull(source, Messages.NULL_ENUMERATOR_SOURCE); return of(source.iterator()); } /** * Returns an enumerator enumerating over the elements of an existing * {@code Spliterator}. * * @param <E> the type of elements being enumerated. * @param source the {@link Spliterator} being enumerated upon. * @return the new enumerator. * @exception IllegalArgumentException {@code source} is null. */ public static <E> Enumerator<E> of(Spliterator<E> source) { Checks.ensureNotNull(source, Messages.NULL_ENUMERATOR_SOURCE); return of(Spliterators.iterator(source)); } /** * Returns an enumerator enumerating over the elements supplied by an * existing {@code Supplier}. * <p> * The enumerator extracts and returns elements from <code>source</code> * up to the first empty {@link Optional} value. * </p> * * @param <E> the type of elements being enumerated. * @param source the {@link Supplier} of elements being enumerated. * Enumeration continues up to the first empty {@link Optional}. * @return the new enumerator. * @exception IllegalArgumentException {@code source} is null. */ public static <E> Enumerator<E> of(Supplier<Optional<E>> source) { return new SuppliedEnumerator(source); } /** * Returns an enumerator enumerating over an {@link Iterator} supplied * lazily. * <p> * This method works like {@link #of(java.util.Iterator)} with the * distinction that <code>source</code> is supplied by a {@link Supplier}. * </p> * * @param <E> the type of elements being enumerated. * @param source the {@link Supplier} supplying the {@link Iterator} to * enumerate upon. * @return the new enumerator. * @see #of(java.util.Iterator) * @exception IllegalArgumentException {@code source} is null. */ public static <E> Enumerator<E> ofLazyIterator(Supplier<? extends Iterator<E>> source) { return LazyEnumerator.of(source); } /** * Returns an enumerator enumerating over an {@code Iterable} supplied * lazily. * <p> * This method works like {@link #of(java.lang.Iterable)} with the * distinction that <code>source</code> is supplied by a {@link Supplier}. * </p> * * @param <E> the type of elements being enumerated. * @param source the {@code Supplier} supplying the {@link Iterable} to * enumerate upon. * @return the new enumerator. * @see #of(java.lang.Iterable) * @exception IllegalArgumentException {@code source} is null. */ public static <E> Enumerator<E> ofLazyIterable(Supplier<? extends Iterable<E>> source) { Checks.ensureNotNull(source, Messages.NULL_ENUMERATOR_SOURCE); return LazyEnumerator.of(() -> of(source.get())); } /** * Returns an enumerator enumerating over an {@code Enumeration} supplied * lazily. * <p> * This method works like {@link #of(java.util.Enumeration)} with the * distinction that {@code source} is supplied by a {@link Supplier}. * </p> * * @param <E> the type of elements being enumerated * @param source the {@code Supplier} supplying the {@link Enumeration} to * enumerate upon * @return the new enumerator * @see #of(java.util.Enumeration) * @exception IllegalArgumentException {@code source} is null. */ public static <E> Enumerator<E> ofLazyEnumeration(Supplier<? extends Enumeration<E>> source) { Checks.ensureNotNull(source, Messages.NULL_ENUMERATOR_SOURCE); return LazyEnumerator.of(() -> of(source.get())); } /** * Returns an enumerator enumerating over a {@code Stream} supplied lazily. * <p> * This method works like {@link #of(java.util.stream.Stream)} with the * distinction that {@code source} is supplied by a {@link Supplier}. * </p> * * @param <E> the type of elements being enumerated. * @param source the {@code Supplier} supplying the {@link Stream} to * enumerate upon. * @return the new enumerator. * @see #of(java.util.stream.Stream) * @exception IllegalArgumentException {@code source} is null. */ public static <E> Enumerator<E> ofLazyStream(Supplier<? extends Stream<E>> source) { Checks.ensureNotNull(source, Messages.NULL_ENUMERATOR_SOURCE); return LazyEnumerator.of(() -> of(source.get())); } /** * Returns an enumerator enumerating over a {@code Spliterator} supplied * lazily. * <p> * This method works like {@link #of(java.util.Spliterator)} with the * distinction that {@code source} is supplied by a {@link Supplier}. * </p> * * @param <E> the type of elements being enumerated. * @param source the {@code Supplier} supplying the {@link Spliterator} to * enumerate upon. * @return the new enumerator. * @see #of(java.util.Spliterator) * @exception IllegalArgumentException {@code source} is null. */ public static <E> Enumerator<E> ofLazySpliterator(Supplier<? extends Spliterator<E>> source) { Checks.ensureNotNull(source, Messages.NULL_ENUMERATOR_SOURCE); return LazyEnumerator.of(() -> of(source.get())); } /** * Returns an enumerator that supports late-binding. * * @param <E> type of enumerated elements. * @param clazz class of enumerated elements. * @return the new enumerator. * @see LateBindingEnumerator */ public static <E> LateBindingEnumerator<E> ofLateBinding(Class<E> clazz) { return new LateBindingEnumerator<E>(); } // ---------------------------------------------------------------------- // /** * Casts the current enumerator to an enumerator of a different parameter * type. * <p> * Casting takes place upon the enumerator only. Element casting is * considered implicit. * <p> * <em>This operation is highly composable.</em> * </p> * * @param <T> type of elements of the resulted enumerator. * @param clazz class of the type to cast to. * @return the type-casted enumerator. * @see #asFiltered(java.lang.Class) */ public default <T> Enumerator<T> as(Class<T> clazz) { Checks.ensureNonEnumerating(this); return (Enumerator<T>) this; } /** * Casts the current enumerator to a different parameter type and filters * the enumerated elements based on that type. * <p> * This method works exactly like * {@code this.filter(e -> clazz.isInstance(e)).as(clazz)}. * </p> * * @param <T> type of elements of the resulted enumerator. * @param clazz class of the type to cast and filter to. * @return the resulted enumerator. * @see #as(java.lang.Class) * @see #filter(java.util.function.Predicate) */ public default <T> Enumerator<T> asFiltered(Class<T> clazz) { Checks.ensureNotNull(clazz, Messages.NULL_ENUMERATOR_CLASS); return filter(clazz::isInstance).as(clazz); } /** * Returns a {@code OnceEnumerable} instance enumerating over the current * enumerator. * <p> * This method satisfies the condition * <code>this.asEnumerable().iterator() == this</code>. * </p> * * @return the new {@link OnceEnumerable}. */ public default OnceEnumerable<E> asEnumerable() { Checks.ensureNonEnumerating(this); return new OnceEnumerable<E>(this); } /** * Returns an {@code Enumeration} enumerating over the current enumerator. * * @return the new {@link Enumeration}. * @see #asEnumerable() */ public default Enumeration<E> asEnumeration() { Checks.ensureNonEnumerating(this); return new EnumerableEnumeration(this); } /** * Returns an infinite enumerator of non-empty {@code Optional} instances * containing the current elements as long as they last or empty * {@code Optional} instances if the current enumerator has no more * elements. * <p> * The current enumerator may not return null elements. * <p> * <em>This operation is highly composable.</em> * </p> * @return the infinite enumerator of {@link Optional} instances. */ public default Enumerator<Optional<E>> asOptional() { return this.map(Optional::of).concat(Enumerator.of(() -> Optional.of(Optional.empty()))); } /** * Returns a sequential {@code Spliterator} iterating over the current * enumerator. * * @return the new {@link Spliterator}. * @see #asStream() */ public default Spliterator<E> asSpliterator() { Checks.ensureNonEnumerating(this); return Spliterators.spliteratorUnknownSize(this, Spliterator.ORDERED); } /** * Returns a sequential {@code Stream} streaming over the current * enumerator. * * @return the new {@link Stream}. * @see #asSpliterator() * @see #asSupplier() */ public default Stream<E> asStream() { Checks.ensureNonEnumerating(this); return StreamSupport.stream(asSpliterator(), false); } /** * Returns a {@code Supplier} supplying the elements of the current * enumerator. * <p> * The resulted {@link Supplier} supplies non-empty {@link Optional} * instances as long as the current enumerator has elements. The current * enumerator may not yield null elements. * </p> * * @return the new {@code Supplier}. * @see #asStream() */ public default Supplier<Optional<E>> asSupplier() { Checks.ensureNonEnumerating(this); return () -> hasNext() ? Optional.of(next()) : Optional.empty(); } /** * Returns a {@code ShareableEnumerator} sharing the elements of the * current enumerator. * <p> * A {@link ShareableEnumerator} can either enumerate the elements of the * current enumerator itself or it can produce a collection of * {@link Enumerator} instances that will share the elements of the * current iterator when enumerating independently of each other. The two * modes are mutually exclusive. * </p> * @return the new {@link ShareableEnumerator}. * @see SharingEnumerator */ public default ShareableEnumerator<E> asShareable() { Checks.ensureNonEnumerating(this); return new ShareableEnumerator<E>(this); } /** * Returns a fault-tolerant enumerator with no retries. The resulted * enumerator has the following characteristics: * <ul> * <li>any exception thrown by {@link #next()} gets handled by * calling <code>handler.accept(? super Exception)</code> and it doesn't * stop enumeration</li> * <li>any exception thrown by {@link #hasNext()} gets handled by * calling <code>handler.accept(? super Exception)</code> and it stops * enumeration</li> * <li>any exception thrown by * <code>handler.accept(? super Exception)</code> stops enumeration and * does not re-throw</li> * </ul> * * @param handler {@link Consumer} handling {@link Exception} instances * thrown by {@code hasNext()} or {@code next()}. * @return the new fault-tolerant enumerator. * @exception IllegalArgumentException {@code handler} is null. * @see #asTolerant(java.util.function.Consumer, int) */ public default Enumerator<E> asTolerant(Consumer<? super Exception> handler) { return new TolerantEnumerator(this, handler, 0); } /** * Returns a fault-tolerant enumerator with retries. The resulted * enumerator has the following characteristics: * <ul> * <li>any exception thrown by {@link #next()} gets handled by * calling <code>handler.accept(? super Exception)</code> and it doesn't * stop enumeration</li> * <li>any exception thrown by {@link #hasNext()} gets handled by * calling <code>handler.accept(? super Exception)</code> and it stops * enumeration after a number of retries.</li> * <li>any exception thrown by * <code>handler.accept(? super Exception)</code> stops enumeration and * does not re-throw</li> * </ul> * * @param handler {@link Consumer} handling {@link Exception} instances * thrown by {@code hasNext()} or {@code next()}. * @param retries number of retries to {@code hasNext()} before returning * false if {@code hasNext()} throws. * @return the new fault-tolerant enumerator. * @exception IllegalArgumentException {@code handler} is * null or {@code retries} is negative. * @see #asTolerant(java.util.function.Consumer) */ public default Enumerator<E> asTolerant(Consumer<? super Exception> handler, int retries) { return new TolerantEnumerator(this, handler, retries); } // ---------------------------------------------------------------------- // /** * Returns whether all the elements of the current enumerator match the * provided predicate. * * @param predicate state-less predicate to apply to the enumerated * elements. * @return true if all the elements satisfy. * {@code predicate}, false otherwise * @see #anyMatch(java.util.function.Predicate) * @see #noneMatch(java.util.function.Predicate) * @exception IllegalArgumentException {@code predicate} is null. */ public default boolean allMatch(Predicate<? super E> predicate) { Checks.ensureNotNull(predicate, Messages.NULL_ENUMERATOR_PREDICATE); while (hasNext()) { if (!predicate.test(next())) { return false; } } return true; } /** * Returns whether any elements of the current enumerator match the * provided predicate. * * @param predicate state-less predicate to apply to the enumerated * elements. * @return true if some elements satisfy * {@code predicate}, false otherwise. * @see #allMatch(java.util.function.Predicate) * @see #noneMatch(java.util.function.Predicate) * @exception IllegalArgumentException {@code predicate} is null. */ public default boolean anyMatch(Predicate<? super E> predicate) { Checks.ensureNotNull(predicate, Messages.NULL_ENUMERATOR_PREDICATE); while (hasNext()) { if (predicate.test(next())) { return true; } } return false; } /** * Appends the provided elements to the end of the current enumerator. * <p> * <em>This operation is highly composable.</em> * </p> * * @param elements values to append. * @return the appended enumerator. * @see #concatOn(java.lang.Object...) */ public default Enumerator<E> append(E... elements) { return concat(on(elements)); } /** * Returns an enumerator that chooses elements from the provided iterators. * <p> * This method works like * {@link #choiceOf(java.util.function.IntSupplier, * java.util.function.IntUnaryOperator, * java.util.Iterator, * java.util.Iterator, * java.util.Iterator...)} * with the distinction that {@code altIndexSupplier} is * {@code i -> (i+1)%(2+rest.length)}. * </p> * @param <E> the type of elements being enumerated. * @param indexSupplier {@link Supplier} instance supplying the index of * the iterator to get the next element from. * @param first the first iterator to choose elements from. * @param second the second iterator to choose elements from. * @param rest other iterators to choose elements from. * @return the new enumerator. * @see #choiceOf(java.util.function.IntSupplier, * java.util.function.IntUnaryOperator, * java.util.Iterator, * java.util.Iterator, java.util.Iterator...) * @see #zipAny(java.util.Iterator) * @see #zipBoth(java.util.Iterator) * @see #zipLeft(java.util.Iterator) * @see #zipRight(java.util.Iterator) * @exception IllegalArgumentException any argument is null. */ public static <E> Enumerator<E> choiceOf(IntSupplier indexSupplier, Iterator<E> first, Iterator<? extends E> second, Iterator<? extends E>... rest) { final int size = 1 + 1 + rest.length; final IntUnaryOperator altIndexSupplier = i -> (i + 1) % size; return choiceOf(indexSupplier, altIndexSupplier, first, second, rest); } /** * Returns an enumerator that chooses elements from the provided iterators. * <p> * The pair formed of {@code indexSupplier} and * {@code altIndexSupplier} makes the choice algorithm. The resulted * enumerator chooses its next element in a multi-step tryPipelineOut: * </p> * <ul> * <li>it calls {@code indexSupplier.get()} to get the index of a * provided iterator. That iterator will provide the next element. * Index {@code 0} means the first iterator, index {@code 1} * means the second iterator and so on.</li> * <li>if the chosen iterator has no elements, the resulted enumerator * calls <code>altIndexSupplier.apply(index)</code> repeatedly until * reaching a provided iterator that has elements.</li> * <li>it calls {@link #next()} on the chosen iterator and it returns the * element</li> * </ul> * <p> * The resulted enumerator will always stop when all the supplied iterators * have no elements. Continuing to extract elements requires that a * non-finished participant iterator can be found in * 2+<code>rest.length</code> steps or less. If such an iterator cannot * be found in the required number of steps, then the resulted enumerator * stops even though there may be elements left. * </p> * <p>To avoid early stopping it is necessary that * <code>altIndexSupplier</code> leads to a non-finished iterator in * timely manner. It is the responsibility of the caller to provide the * choice algorithm in such a way that no element is lost because the * resulted enumerator stops too early. * </p> * <pre> * Example: * <code> * Enumerator.choiceOf(() -> 0, * i -> (i+1)%4, * Enumerator.empty<Integer>(), * Enumerator.on(1, 2, 3), * Enumerator.on(4, 5), * Enumerator.on(6)) * </code> * will produce the sequence 1, 2, 3, 4, 5 and 6. * </pre> * @param <E> type of elements being enumerated. * @param indexSupplier {@link Supplier} instance supplying the index of the * provided iterator to get the next element from. * @param altIndexSupplier {@link IntUnaryOperator} instance supplying the * index of the next provided iterator if the iterator chosen by * <code>indexSupplier</code> has no more elements. * @param first the first iterator to choose elements from. * @param second the second iterator to choose elements from. * @param rest other iterators to choose elements from. * @return the new enumerator. * @exception IllegalArgumentException any argument is null. */ public static <E> Enumerator<E> choiceOf(IntSupplier indexSupplier, IntUnaryOperator altIndexSupplier, Iterator<E> first, Iterator<? extends E> second, Iterator<? extends E>... rest) { return new ChoiceEnumerator(indexSupplier, altIndexSupplier, first, second, Arrays.asList(rest)); } /** * Collects the elements of the current enumerator according to the * provided {@code Collector}. * <p> * This methods works exactly like * <code>this.asStream().collect(collector)</code>. * </p> * @param <R> the collected type. * @param <A> the type of the intermediate accumulator during collection. * @param collector {@link Collector} instance collecting the enumerated * elements. * @return the result of the collection. * @see #asStream() * @see Stream * @see Collector * @exception IllegalArgumentException {@code collector} is null. */ public default <R, A> R collect(Collector<? super E, A, R> collector) { Checks.ensureNotNull(collector, Messages.NULL_ENUMERATOR_HANDLER); return asStream().collect(collector); } /** * Concatenates the current enumerator with the provided {@code Iterator}. * <p> * <em>This operation is highly composable.</em> * </p> * * @param elements {@link Iterator} instance to concatenate with * @return the concatenated enumerator. * @exception IllegalArgumentException {@code elements} is null. */ public default Enumerator<E> concat(Iterator<? extends E> elements) { return new PipeEnumerator(this).concat(elements); } /** * Concatenates the current enumerator with the provided {@code Iterable}. * <p> * <em>This operation is highly composable.</em> * </p> * * @param elements {@link Iterable} instance to concatenate with. * @return the concatenated enumerator. * @exception IllegalArgumentException <code>elements</code> is null. */ public default Enumerator<E> concat(Iterable<? extends E> elements) { return concat(of(elements)); } /** * Concatenates the current enumerator with the provided * {@code Enumeration}. * <p> * <em>This operation is highly composable.</em> * </p> * * @param elements {@link Enumeration} instance to concatenate with * @return the concatenated enumerator. * @exception IllegalArgumentException <code>elements</code> is null. */ public default Enumerator<E> concat(Enumeration<? extends E> elements) { return concat(of(elements)); } /** * Concatenates the current enumerator with the provided sequential * {@code Stream}. * <p> * <em>This operation is highly composable.</em> * </p> * * @param elements {@link Stream} instance to concatenate with * @return the concatenated enumerator. * @exception IllegalArgumentException <code>elements</code> is null. */ public default Enumerator<E> concat(Stream<? extends E> elements) { return concat(of(elements)); } /** * Concatenates the current enumerator with the provided sequential * {@code Spliterator}. * <p> * <em>This operation is highly composable.</em> * </p> * * @param elements {@link Spliterator} instance to concatenate with * @return the concatenated enumerator. * @exception IllegalArgumentException <code>elements</code> is null. */ public default Enumerator<E> concat(Spliterator<? extends E> elements) { return concat(of(elements)); } /** * Concatenates the current enumerator with the sequence formed of * the elements supplied by the provided {@code Supplier}. * <p> * The first value supplied by <code>elements</code> that is an empty * {@link Optional} marks the end of the sequence to concatenate to. * <p> * <em>This operation is highly composable.</em> * </p> * * @param elements {@link Supplier} instance supplying the elements to * concatenate to. * @return the concatenated enumerator. * @exception IllegalArgumentException <code>elements</code> is null. */ public default Enumerator<E> concat(Supplier<Optional<E>> elements) { return concat(of(elements)); } /** * Concatenates the current enumerator with the sequence formed of the * provided elements * <p> * This method works exactly like {@link #append(java.lang.Object...)}. * <p> * <em>This operation is highly composable.</em> * </p> * * @param elements the values to concatenate at the end of the current * enumerator. * @return the concatenated enumerator. */ public default Enumerator<E> concatOn(E... elements) { return concat(on(elements)); } /** * Returns whether the current enumerator has an element equalling the * provided value. * * @param element value to look for. * @return true if value equals an element in the current * enumerator, false otherwise. * @exception IllegalArgumentException <code>elements</code> is null. */ public default boolean contains(E element) { while (hasNext()) { if (Objects.equals(element, next())) { return true; } } return false; } /** * Returns the number of elements in the current enumerator. * * @return the number of enumerated elements. */ public default long count() { long cnt = 0; while (hasNext()) { ++cnt; next(); } return cnt; } /** * Returns an enumerator containing the elements of the current enumerator * in the same order by without duplicates. * <p> * <em>This operation is highly composable.</em> * </p> * * @return the new enumerator. * @see #sorted() */ public default Enumerator<E> distinct() { return Reversible.distinct(this, false); } /** * Returns the element at the provided index within the sequence * of enumerated elements. * <p> * An <code>index</code> equal to <code>0</code> means the first element. * This method returns an empty {@link Optional} if <code>index</code> is * larger than or equal to <code>this.count()</code>. * </p> * * @param index {@code 0}-based index for the element to return. * @return {@link Optional} instance containing the element at the * required <code>index</code>. * @exception IllegalArgumentException <code>index</code> is negative. */ public default Optional<E> elementAt(long index) { Checks.ensureNonNegative(index, Messages.NEGATIVE_ENUMERATOR_INDEX); Enumerator<E> skipped = skip(index); return Optional.ofNullable(skipped.hasNext() ? skipped.next() : null); } /** * Returns whether the current enumerator and the provided iterator * have equal elements and in the same order. * <pre> * This method works exactly like: * <code> * this.zipAny(elements) * .matchAll(p -> p.getLeft().isPresent() == p.getRight().isPresent() * && p.getLeft.isPresent() * && Objects.equsls(p.getLeft().get(), * p.getRight().get())) * </code> * </pre> * * @param <T> type of elements to match the enumerated elements against. * @param elements {@link Iterator} instance whose elements get matched * against the enumerated elements. * @return true if the enumerated elements match the * elements of {@code Iterator} in both value and position, * false otherwise * @exception IllegalArgumentException <code>elements</code> is null. */ public default <T> boolean elementsEqual(Iterator<T> elements) { Checks.ensureNotNull(elements, Messages.NULL_ITERATOR); boolean thisHasNext; boolean elementsHasNext; while (true) { thisHasNext = hasNext(); elementsHasNext = elements.hasNext(); if (!thisHasNext || !elementsHasNext) { return thisHasNext == elementsHasNext; } final E thisElement = next(); final T otherElement = elements.next(); if (!Objects.equals(thisElement, otherElement)) { return false; } } } /** * Returns en enumerator with no elements. * * @param <E> type of the enumerated elements. * @return the empty enumerator. */ public static <E> Enumerator<E> empty() { return of(Collections.emptyIterator()); } /** * Returns an enumerator consisting of the elements of the current * enumerator that match the provided predicate. * <p> * <em>This operation is highly composable.</em> * </p> * * @param predicate state-less predicate to apply on each element. * @return the filtered enumerator. */ public default Enumerator<E> filter(Predicate<? super E> predicate) { Checks.ensureNotNull(predicate, Messages.NULL_ENUMERATOR_PREDICATE); return new PipeEnumerator(this).filter(predicate); } /** * Returns the first element of the current enumerator, if any. * * @return {@link Optional} instance containing the first element. */ public default Optional<E> first() { return hasNext() ? Optional.of(next()) : Optional.empty(); } /** * Returns an enumerator consisting of the results of replacing each * enumerated element with the content of a mapped enumerator obtained * by applying the provided mapper on each enumerated element. * <p> * <em>This operation is highly composable.</em> * </p> * <pre> * Example: * <code> * Enumerator.on(1, 2, 3) * .flatMap(i -> Enumerator.on(i*i, i*(i+1))) * </code> * will produce the sequence 1, 2, 4, 6, 9 and 12. * </pre> * * @param <R> the element type of the new enumerator. * @param mapper {@link Function} instance to apply on each enumerated * element of the current enumerator. * @return the flattened enumerator. * @exception IllegalArgumentException <code>mapper</code> is null. */ public default <R> Enumerator<R> flatMap(Function<? super E, ? extends Iterator<? extends R>> mapper) { Checks.ensureNotNull(mapper, Messages.NULL_ENUMERATOR_MAPPER); return new PipeEnumerator(this).flatMap(mapper); } /** * Performs an action on each enumerated element. * * @param consumer action to perform on each enumerated element. * @exception IllegalArgumentException <code>consumer</code> is null. */ public default void forEach(Consumer<? super E> consumer) { Checks.ensureNotNull(consumer, Messages.NULL_ENUMERATOR_CONSUMER); while (hasNext()) { consumer.accept(next()); } } /** * Returns an infinite enumerator obtained by applying repeatedly the * provided unary operator. * <p> * The resulted enumerator returns <code>seed</code>, * <code>f(seed)</code>, <code>f(f(seed))</code> ... * </p> * * @param <E> the type of enumerated elements * @param seed the initial element * @param f state-less {@link Function} instance to apply on the previous * element to obtain the next element * @return the iterated enumerator * @exception IllegalArgumentException <code>f</code> is null. */ public static <E> Enumerator<E> iterate(E seed, UnaryOperator<E> f) { Checks.ensureNotNull(f, Messages.NULL_ENUMERATOR_GENERATOR); final Mutable<E> result = new MutableObject(seed); final MutableBoolean first = new MutableBoolean(true); return of(() -> { if (first.booleanValue()) { first.setValue(false); } else { result.setValue(f.apply(result.getValue())); } return Optional.of(result.getValue()); }); } /** * Returns the last element of the current enumerator, if any. * * @return {@link Optional} instance containing the last element. */ public default Optional<E> last() { E result = null; while (hasNext()) { result = next(); } return Optional.ofNullable(result); } /** * Returns the current enumerator truncated to the given size. * <p> * This method works exactly like <code>take(long)</code>. * <p> * <em>This operation is highly composable.</em> * </p> * * @param maxSize the maximum number of elements in the resulted * enumerator. * @return the truncated enumerator. * @exception IllegalArgumentException <code>maxSize</code> is negative/ */ public default Enumerator<E> limit(long maxSize) { return new PipeEnumerator(this).limit(maxSize); } /** * Returns an enumerator enumerating over the elements of the current * enumerator while stopping at the first element that does not match * the provided predicate. * <p> * this method works exactly like * {@link #takeWhile(java.util.function.Predicate)}. * <p> * <em>This operation is highly composable.</em> * </p> * * @param predicate state-less {@link Predicate} instance to apply on * enumerated elements. * @return the truncated enumerator. * @exception IllegalArgumentException <code>predicate</code> is null. */ public default Enumerator<E> limitWhile(Predicate<? super E> predicate) { return takeWhile(predicate); } /** * Returns an enumerator consisting of the results of applying the given * function on the enumerated elements. * <p> * <em>This operation is highly composable.</em> * </p> * * @param <R> the element type of the new enumerator. * @param mapper state-less {@link Function} to apply on each enumerated * element. * @return the mapped enumerator * @see #map(java.util.function.BiFunction) */ public default <R> Enumerator<R> map(Function<? super E, ? extends R> mapper) { return new PipeEnumerator(this).map(mapper); } /** * Returns an enumerator consisting of the results of applying the * provided function on the enumerated elements. * <p> * This method works like {@link #map(java.util.function.Function) } with * the distinction that each enumerated element is accompanied by its * <code>0</code>-based index. * </p> * * @param <R> the element type of the resulted enumerator. * @param mapper state-less {@link BiFunction} to apply on each enumerated * element. * @return the mapped enumerator. * @exception IllegalArgumentException <code>mapper</code> is null. * @see #map(java.util.function.Function) */ public default <R> Enumerator<R> map(BiFunction<? super E, ? super Long, ? extends R> mapper) { return Reversible.map(this, mapper, false); } /** * Returns the maximum of the enumerated elements according to the provided * comparator. * * @param comparator state-less {@link Comparator} instance * to compare enumerated elements. * @return {@link Optional} instance containing the maximal enumerated * element. * @exception IllegalArgumentException <code>comparator</code> * is null. * @see #min(java.util.Comparator) */ public default Optional<E> max(Comparator<? super E> comparator) { Checks.ensureNotNull(comparator, Messages.NULL_ENUMERATOR_COMPARATOR); E m = null; while (hasNext()) { E e = next(); m = (m == null || comparator.compare(e, m) > 0) ? e : m; } return Optional.ofNullable(m); } /** * Returns the minimum of the enumerated elements according to the provided * comparator. * * @param comparator state-less {@link Comparator} instance * to compare enumerated elements. * @return {@link Optional} instance containing the minimal enumerated * element. * @exception IllegalArgumentException <code>comparator</code> * is null. * @see #max(java.util.Comparator) */ public default Optional<E> min(Comparator<? super E> comparator) { E m = null; while (hasNext()) { E e = next(); m = (m == null || comparator.compare(e, m) < 0) ? e : m; } return Optional.ofNullable(m); } /** * Returns whether no enumerated element matches the provided predicate. * * @param predicate state-less {@link Predicate} instance to apply * to enumerated elements. * @return true if no enumerated element matches * <code>predicate</code>, false otherwise. * @exception IllegalArgumentException <code>predicate</code> is null. * @see #allMatch(java.util.function.Predicate) * @see #anyMatch(java.util.function.Predicate) */ public default boolean noneMatch(Predicate<? super E> predicate) { Checks.ensureNotNull(predicate, Messages.NULL_ENUMERATOR_PREDICATE); while (hasNext()) { if (predicate.test(next())) { return false; } } return true; } /** * Returns an enumerator with the same enumerated elements as the current * stream, additionally performing the provided action on each enumerated * element. * <p> * <em>This operation is highly composable.</em> * </p> * * @param action {@link Consumer} instance to apply on the elements as they * get enumerated. * @return the peeked enumerator. * @exception IllegalArgumentException <code>action</code> is null. */ public default Enumerator<E> peek(Consumer<? super E> action) { return Reversible.peek(this, action, false); } /** * Prepends the current enumerator with the provided {@link Iterator}. * <p> * <em>This operation is highly composable.</em> * </p> * * @param elements {@link Iterator} instance to prepend in front of the * current enumerator. * @return the prepended enumerator. * @exception IllegalArgumentException <code>elements</code> is null. */ public default Enumerator<E> prepend(Iterator<? extends E> elements) { return new PipeEnumerator(this).reversedConcat(elements); } /** * Prepends the current enumerator with the provided {@link Iterable}. * <p> * <em>This operation is highly composable.</em> * </p> * * @param elements {@link Iterable} instance to prepend in front of the * current enumerator. * @return the prepended enumerator. * @exception IllegalArgumentException <code>elements</code> is null. */ public default Enumerator<E> prepend(Iterable<? extends E> elements) { return prepend(of(elements)); } /** * Prepends the current enumerator with the provided * {@link Enumeration}. * <p> * <em>This operation is highly composable.</em> * </p> * * @param elements {@link Enumeration} instance to prepend in front of the * current enumerator. * @return the prepended enumerator. * @exception IllegalArgumentException <code>elements</code> is null. */ public default Enumerator<E> prepend(Enumeration<? extends E> elements) { return prepend(of(elements)); } /** * Prepends the current enumerator with the provided sequential * {@link Stream}. * <p> * <em>This operation is highly composable.</em> * </p> * * @param elements {@link Stream} instance to prepend in front of the * current enumerator. * @return the prepended enumerator. * @exception IllegalArgumentException <code>elements</code> is null. */ public default Enumerator<E> prepend(Stream<? extends E> elements) { return prepend(of(elements)); } /** * Prepends the current enumerator with the provided sequential * {@link Spliterator}. * <p> * <em>This operation is highly composable.</em> * </p> * * @param elements {@link Spliterator} instance to prepend in front of the * current enumerator. * @return the prepended enumerator. * @exception IllegalArgumentException <code>elements</code> is null. */ public default Enumerator<E> prepend(Spliterator<? extends E> elements) { return prepend(of(elements)); } /** * Prepends the current enumerator with the sequence formed of * the elements supplied by the provided {@code Supplier}. * <p> * The first value supplied by <code>elements</code> that is an empty * {@link Optional} marks the end of the sequence to prepend with. * <p> * <em>This operation is highly composable.</em> * </p> * * @param elements {@link Supplier} instance supplying the elements to * prepend in front of the current enumerator. * @return the prepended enumerator. * @exception IllegalStateException the current enumerator is enumerating. * @exception IllegalArgumentException <code>elements</code> is null. */ public default Enumerator<E> prepend(Supplier<Optional<E>> elements) { return prepend(of(elements)); } /** * Prepends the current enumerator with the sequence formed of the provided * elements. * <p> * This method works like <code>concatOn(elements)</code> but at the * opposite end. * <p> * <em>This operation is highly composable.</em> * </p> * * @param elements the values to prepend at the beginning of the current * enumerator. * @return the prepended enumerator. */ public default Enumerator<E> prependOn(E... elements) { return prepend(on(elements)); } /** * Returns an enumerator enumerating over an ordered sequence between * a start and an exclusive end. * * @param <E> type of enumerated elements. * @param startInclusive the first element in the sequence. * @param endExclusive the exclusive sequence end. * @param succ {@link UnaryOperator} instance returning the successor of * a given element. * @param cmp {@link Comparator} instance comparing enumerated elements * @return the enumerator covering the range. * @exception IllegalArgumentException <code>succ</code> or <code>cmp</code> * is null. */ public static <E> Enumerator<E> range(E startInclusive, E endExclusive, UnaryOperator<E> succ, Comparator<? super E> cmp) { Checks.ensureNotNull(succ, Messages.NULL_ENUMERATOR_GENERATOR); Checks.ensureNotNull(cmp, Messages.NULL_ENUMERATOR_COMPARATOR); return cmp.compare(startInclusive, endExclusive) >= 0 ? Enumerator.empty() : iterate(startInclusive, succ).takeWhile(e -> cmp.compare(e, endExclusive) < 0); } /** * Returns an enumerator enumerating over an ordered sequence between * a start and an inclusive end. * * @param <E> type of enumerated elements. * @param startInclusive the first element in the sequence. * @param endInclusive the last element in the sequence. * @param succ {@link UnaryOperator} instance returning the successor of * a given element. * @param cmp {@link Comparator} instance comparing enumerated elements * @return the enumerator covering the range. * @exception IllegalArgumentException <code>succ</code> or <code>cmp</code> * is null. */ public static <E> Enumerator<E> rangeClosed(E startInclusive, E endInclusive, UnaryOperator<E> succ, Comparator<? super E> cmp) { Checks.ensureNotNull(succ, Messages.NULL_ENUMERATOR_GENERATOR); Checks.ensureNotNull(cmp, Messages.NULL_ENUMERATOR_COMPARATOR); return cmp.compare(startInclusive, endInclusive) > 0 ? Enumerator.empty() : iterate(startInclusive, succ).takeWhile(e -> cmp.compare(e, endInclusive) <= 0); } /** * Returns an enumerator enumerating over an integral range, upper limit * excluded. * * @param startInclusive the lower bound of the range. * @param endExclusive the exclusive upper bound of the range. * @return the enumerator covering the range. */ public static IntRangeEnumerator rangeInt(int startInclusive, int endExclusive) { return new IntRangeEnumerator(startInclusive, endExclusive, false); } /** * Returns an enumerator enumerating over an integral range, upper limit * included. * * @param startInclusive the lower bound of the range. * @param endInclusive the inclusive upper bound of the range. * @return the enumerator covering the range. */ public static IntRangeEnumerator rangeIntClosed(int startInclusive, int endInclusive) { return new IntRangeEnumerator(startInclusive, endInclusive, true); } /** * Returns an enumerator enumerating over an long integral range, upper * limit excluded. * * @param startInclusive the lower bound of the range. * @param endExclusive the exclusive upper bound of the range. * @return the enumerator covering the range. */ public static LongRangeEnumerator rangeLong(long startInclusive, long endExclusive) { return new LongRangeEnumerator(startInclusive, endExclusive, false); } /** * Returns an enumerator enumerating over an long integral range, upper * limit included. * * @param startInclusive the lower bound of the range. * @param endInclusive the inclusive upper bound of the range. * @return the enumerator covering the range. */ public static LongRangeEnumerator rangeLongClosed(long startInclusive, long endInclusive) { return new LongRangeEnumerator(startInclusive, endInclusive, true); } /** * Performs a reduction on enumerated elements using an associative * accumulation function and returns the reduced value, if any. * * @param accumulator associative {@link BinaryOperator} combining two * enumerated elements. * @return {@link Optional} instance containing the reduced value. * @exception IllegalArgumentException <code>accumulator</code> is null. */ public default Optional<E> reduce(BinaryOperator<E> accumulator) { Checks.ensureNotNull(accumulator, Messages.NULL_ENUMERATOR_ACCUMULATOR); if (!hasNext()) { return Optional.empty(); } final E first = next(); return Optional.of(reduce(first, accumulator)); } /** * Performs a reduction on enumerated elements using an identity element * and an associative accumulation function and returns the reduced value. * * @param identity starting point for the reduction. * @param accumulator associative {@link BinaryOperator} combining * two enumerated elements. * @return the reduced value. * @exception IllegalArgumentException <code>accumulator</code> is null. */ public default E reduce(E identity, BinaryOperator<E> accumulator) { Checks.ensureNotNull(accumulator, Messages.NULL_ENUMERATOR_ACCUMULATOR); E result = identity; while (hasNext()) { result = accumulator.apply(result, next()); } return result; } /** * Returns an enumerator that enumerates the same element the given number * of times. * * @param <E> type of enumerated element. * @param element the element to return. * @param count the number of elements to return. * @return the enumerator repeating the element. * @exception IllegalArgumentException <code>count</code> is negative. * @see #repeatAll(java.util.function.Supplier, long) * @see #repeatAll(int) * @see #repeatEach(int) */ public static <E> Enumerator<E> repeat(E element, long count) { Checks.ensureNonNegative(count, Messages.NEGATIVE_ENUMERATOR_SIZE); return rangeLong(0, count).map(l -> element); } /** * Returns an enumerator that enumerates over the iterator supplied lazily * the given number of times. * * @param <E> type of enumerated element. * @param source supplier of the iterator to repeat. * @param count the number of times to repeat. * @return the repeated enumerator. * @exception IllegalArgumentException <code>source</code> is * null or <code>count</code> is negative. * @see #repeat(java.lang.Object, long) * @see #repeatAll(int) * @see #repeatEach(int) */ public static <E> Enumerator<E> repeatAll(Supplier<Iterator<E>> source, long count) { Checks.ensureNotNull(source, Messages.NULL_ENUMERATOR_SOURCE); Checks.ensureNonNegative(count, Messages.NEGATIVE_ENUMERATOR_EXPECTED_COUNT); return rangeLong(0, count).flatMap(l -> source.get()); } /** * Returns an enumerator that keeps enumerating over the current sequence, * a given number of times. * <p> * The current enumerator gets iterated only once. * </p> * * @param count the number of times to repeatAll the <code>elements</code> * sequence. * @return the repeating enumerator. * @exception IllegalArgumentException <code>elements</code> is * <code>null</code> or <code>count</code> is negative. * @see #repeat(java.lang.Object, long) * @see #repeatAll(java.util.function.Supplier, long) * @see #repeatEach(int) */ public default Enumerator<E> repeatAll(int count) { Checks.ensureNonNegative(count, Messages.NEGATIVE_ENUMERATOR_EXPECTED_COUNT); final Enumerator<E>[] sharing = asShareable().share(count); return Enumerator.rangeInt(0, sharing.length).flatMap(i -> sharing[i]); } /** * Enumerates over the elements of the current enumerator the given number * of times. * <p> * <em>This operation is highly composable.</em> * </p> * * @param count the number of times to repeatAll the elements in the * sequence. * @return the enumerator of repeated elements. * @exception IllegalArgumentException <code>elements</code> is * <code>null</code> or <code>count</code> is negative. * @see #repeat(java.lang.Object, long) * @see #repeatAll(java.util.function.Supplier, long) * @see #repeatAll(int) */ public default Enumerator<E> repeatEach(int count) { Checks.ensureNonNegative(count, Messages.NEGATIVE_ENUMERATOR_EXPECTED_COUNT); return flatMap(e -> repeat(e, count)); } /** * Returns an enumerator that reverses the order of the enumerated elements. * * @return the reversed enumerator. */ public default Enumerator<E> reverse() { final LinkedList<E> elements = new LinkedList(); while (hasNext()) { elements.add(next()); } return Enumerator.of(elements.descendingIterator()); } /** * Returns the one and only element of the current enumerator. * * @return the single element. * @exception InputMismatchException the current enumerator has * <code>0</code> or more than <code>1</code> elements. */ public default E single() { if (!hasNext()) { throw new InputMismatchException(Messages.NO_SINGLE_ENUMERATOR_ELEMENT); } final E result = next(); if (hasNext()) { throw new InputMismatchException(Messages.NO_SINGLE_ENUMERATOR_ELEMENT); } return result; } /** * Returns the current enumerator with the given number of front elements * dropped. * <p> * <em>This operation is highly composable.</em> * </p> * * @param n the number of front elements to drop. * @return the truncated enumerator. */ public default Enumerator<E> skip(long n) { return new PipeEnumerator(this).skip(n); } /** * Returns the current enumerator with front elements dropped as long as * they match a given predicate. * <p> * <em>This operation is highly composable.</em> * </p> * * @param predicate {@link Predicate} instance to match the enumerated * elements against. * @return the enumerator with front elements dropped. */ public default Enumerator<E> skipWhile(Predicate<? super E> predicate) { return new PipeEnumerator(this).skipWhile(predicate); } /** * Returns an enumerator the sorts the enumerated elements. * * @return the sorted enumerator. */ public default Enumerator<E> sorted() { return of(asStream().sorted()); } /** * Returns an enumerator that sorts the current elements according to the * given comparator. * * @param comparator {@link Comparator} instance to compare enumerated * elements. * @return the sorted enumerator. * @exception IllegalArgumentException <code>comparator</code> is null. */ public default Enumerator<E> sorted(Comparator<? super E> comparator) { Checks.ensureNotNull(comparator, Messages.NULL_ENUMERATOR_COMPARATOR); return of(asStream().sorted(comparator)); } /** * Returns the current enumerator truncated to the given size. * <p> * This method works exactly like <code>limit(long)</code>. * <p> * <em>This operation is highly composable.</em> * </p> * * @param n the maximum number of elements in the resulted enumerator. * @return the truncated enumerator. * @exception IllegalArgumentException <code>n</code> is negative. */ public default Enumerator<E> take(long n) { return limit(n); } /** * Returns an enumerator enumerating over the elements of the current * enumerator while stopping at the first element that does not match * the provided predicate. * <p> * this method works exactly like <code>limitWhile(Predicate)</code>. * <p> * <em>This operation is highly composable.</em> * </p> * * @param predicate state-less {@link Predicate} instance to apply on * enumerated elements. * @return the truncated enumerator. * @exception IllegalArgumentException <code>predicate</code> is null. */ public default Enumerator<E> takeWhile(Predicate<? super E> predicate) { return new PipeEnumerator(this).takeWhile(predicate); } /** * Collects the current enumerator into an array of elements of given class. * * @param clazz {@link Class} of array elements. * @return the array of elements. * @exception IllegalArgumentException <code>clazz</code> is null. */ public default E[] toArray(Class<E> clazz) { Checks.ensureNotNull(clazz, Messages.NULL_ENUMERATOR_CLASS); return toList().toArray((E[]) Array.newInstance(clazz, 0)); } /** * Collects the current enumerator into a list of elements. * * @return the list of elements. */ public default List<E> toList() { return collect(Collectors.toList()); } /** * Collects the current enumerator into a {@code Map} according to * a key mapping function and a value mapping function. * * @param <K> the map key type. * @param <V> the map value type. * @param keyMapper {@link Function} instance extracting the map key * from enumerated elements. * @param valueMapper {@code Function} instance extracting the map value * from enumerated elements. * @return {@link Map} instance containing the results of key and value * mappings. */ public default <K, V> Map<K, V> toMap(Function<? super E, K> keyMapper, Function<? super E, V> valueMapper) { return collect(Collectors.toMap(keyMapper, valueMapper)); } /** * Collects the current enumerator into a set. * * @return the set of elements. */ public default Set<E> toSet() { return collect(Collectors.toSet()); } /** * Returns an enumerator enumerating over the current enumerator and * the provided enumerator, all duplicates removed. * <p> * This method works exactly like {@code this.concat(others).distinct()}. * </p> * * @param others enumerator to perform union with. * @return the unified enumerator. * @exception IllegalArgumentException <code>others</code> is null. */ public default Enumerator<E> union(Iterator<? extends E> others) { return concat(others).distinct(); } /** * Returns an enumerator enumerating over the current enumerator and * the provided {@code Iterable}, all duplicates removed. * <p> * This method works exactly like {@code this.union(on(others))}. * </p> * * @param others {@link Iterable} to perform union with. * @return the unified enumerator. * @exception IllegalArgumentException <code>others</code> is null. */ public default Enumerator<E> union(Iterable<? extends E> others) { return union(of(others)); } /** * Returns an enumerator enumerating over the current enumerator and * the provided {@code Enumeration}, all duplicates removed. * <p> * This method works exactly like {@code this.union(on(others))}. * </p> * * @param others {@link Enumeration} to perform union with. * @return the unified enumerator. * @exception IllegalArgumentException <code>others</code> is null. */ public default Enumerator<E> union(Enumeration<? extends E> others) { return union(of(others)); } /** * Returns an enumerator enumerating over the current enumerator and * the provided {@code Stream}, all duplicates removed. * <p> * This method works exactly like {@code this.union(on(others))}. * </p> * * @param others {@link Stream} to perform union with. * @return the unified enumerator. * @exception IllegalArgumentException <code>others</code> is null. */ public default Enumerator<E> union(Stream<? extends E> others) { return union(of(others)); } /** * Returns an enumerator enumerating over the current enumerator and * the provided {@code Spliterator}, all duplicates removed. * <p> * This method works exactly like {@code this.union(on(others))}. * </p> * * @param others {@link Spliterator} to perform union with. * @return the unified enumerator. * @exception IllegalArgumentException <code>others</code> is null. */ public default Enumerator<E> union(Spliterator<? extends E> others) { return union(of(others)); } /** * Returns an enumerator enumerating over the current enumerator and * the elements supplied by the provided {@code Supplier}, all duplicates * removed. * <p> * This method works exactly like {@code this.union(on(others))}. * </p> * * @param others {@link Supplier} to perform union with. * @return the unified enumerator. * @exception IllegalArgumentException <code>others</code> is null. */ public default Enumerator<E> union(Supplier<Optional<E>> others) { return union(of(others)); } /** * Returns an enumerator enumerating over the current enumerator * and the given elements, all duplicates removed. * <p> * This method works exactly like {@code this.union(on(others))}. * </p> * * @param others elements to perform union with. * @return the unified enumerator. */ public default Enumerator<E> unionOn(E... others) { return union(on(others)); } /** * Returns an enumerator consisting of pairs of elements from the * current enumerator and the given {@code Iterator}, while any * of them has elements. * <p> * The resulted enumerator stops when both the current enumerator and the * provided {@link Iterator} stop. The first element of a returned * pair is empty {@link Optional} if the current enumerator has no * elements. Likewise, the second element of a returned pair is empty * {@code Optional} if the provided {@code Iterator} has no elements. * <p> * <em>This operation is highly composable.</em> * </p> * * @param <T> the of elements to zip with. * @param elements {@code Iterator} to zip with. * @return the zipped enumerator. * @see #zipBoth(java.util.Iterator) * @see #zipLeft(java.util.Iterator) * @see #zipRight(java.util.Iterator) * @exception IllegalArgumentException <code>elements</code> is null. */ public default <T> Enumerator<Pair<Optional<E>, Optional<T>>> zipAny(Iterator<T> elements) { return zipAll((Iterator<E>) elements).map(arr -> Pair.of(arr[0], (Optional<T>) arr[1])); } /** * Returns an enumerator consisting of pairs of elements from the * current enumerator and the given {@code Iterator}, while both * have elements. * <p> * The resulted enumerator stops when aby of the current enumerator or the * provided {@link Iterator} stop. * <p> * <em>This operation is highly composable.</em> * </p> * * @param <T> the of elements to zip with. * @param elements {@code Iterator} to zip with. * @return the zipped enumerator. * @see #zipAny(java.util.Iterator) * @see #zipLeft(java.util.Iterator) * @see #zipRight(java.util.Iterator) * @exception IllegalArgumentException <code>elements</code> is null. */ public default <T> Enumerator<Pair<E, T>> zipBoth(Iterator<T> elements) { return zipAll((Iterator<E>) elements).takeWhile(arr -> arr[0].isPresent() && arr[1].isPresent()) .map(arr -> Pair.of(arr[0].get(), ((Optional<T>) arr[1]).get())); } /** * Returns an enumerator consisting of pairs of elements from the * current enumerator and the given {@code Iterator}, while the current * enumerator has elements. * <p> * The resulted enumerator stops when the current enumerator stops. * The second element of a returned pair is empty {@link Optional} if * the provided {@link Iterator} has no elements. * <p> * <em>This operation is highly composable.</em> * </p> * @param <T> the of elements to zip with. * @param elements {@code Iterator} to zip with. * @return the zipped enumerator. * @see #zipAny(java.util.Iterator) * @see #zipBoth(java.util.Iterator) * @see #zipRight(java.util.Iterator) * @exception IllegalArgumentException <code>elements</code> is null. */ public default <T> Enumerator<Pair<E, Optional<T>>> zipLeft(Iterator<T> elements) { return zipAll((Iterator<E>) elements).takeWhile(arr -> arr[0].isPresent()) .map(arr -> Pair.of(arr[0].get(), (Optional<T>) arr[1])); } /** * Returns an enumerator consisting of pairs of elements from the * current enumerator and the given {@code Iterator}, while the provided * iterator has elements. * <p> * The resulted enumerator stops when the provided {@link Iterator} stops. * The first element of a returned pair is empty {@link Optional} if the * current enumerator has no elements. * </p> * <p> * <em>This operation is highly composable.</em> * </p> * @param <T> the of elements to zip with. * @param elements {@code Iterator} to zip with. * @return the zipped enumerator. * @see #zipAny(java.util.Iterator) * @see #zipBoth(java.util.Iterator) * @see #zipLeft(java.util.Iterator) * @exception IllegalArgumentException <code>elements</code> is null. */ public default <T> Enumerator<Pair<Optional<E>, T>> zipRight(Iterator<T> elements) { return zipAll((Iterator<E>) elements).takeWhile(arr -> arr[1].isPresent()) .map(arr -> Pair.of(arr[0], ((Optional<T>) arr[1]).get())); } /** * Returns an enumerator consisting of an array of {@code Optional} * objects containing elements from the current enumerator and * the given {@code Iterator} instances, while any has elements. * <p> * <em>This operation is highly composable.</em> * </p> * @param first first {@link Iterator} to zip with. * @param rest the rest of iterators to zip with. * @return the zipped enumerator of arrays of {@link Optional} instances. */ public default Enumerator<Optional<E>[]> zipAll(Iterator<? extends E> first, Iterator<? extends E>... rest) { Checks.ensureNotNull(first, Messages.NULL_ITERATOR); for (Iterator<?> it : rest) { Checks.ensureNotNull(it, Messages.NULL_ITERATOR); } return new PipeEnumerator(this).zipAll(first, rest); } }