package com.github.scr.j8iterables; import com.github.scr.j8iterables.core.ConsumingIdentity; import com.github.scr.j8iterables.core.Ends; import com.github.scr.j8iterables.core.J8PrimitiveIterable; import com.github.scr.j8iterables.core.PeekIterator; import com.github.scr.j8iterables.core.StreamIterable; import com.github.scr.j8iterables.core.SupplierIterable; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.FluentIterable; import com.google.common.collect.Iterables; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.ArrayDeque; import java.util.Collection; import java.util.Collections; import java.util.Deque; import java.util.Iterator; import java.util.List; import java.util.NavigableSet; import java.util.Optional; import java.util.PrimitiveIterator; import java.util.Spliterator; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.BinaryOperator; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.function.ToDoubleFunction; import java.util.function.ToIntFunction; import java.util.function.ToLongFunction; import java.util.stream.Collector; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; import java.util.stream.Stream; import java.util.stream.StreamSupport; /** * Utility methods to extend Guava Iterables with Java 8 Stream-like classes such as Collectors. * * @author scr */ @SuppressWarnings({"WeakerAccess", "Guava"}) public class J8Iterables { /** * The empty iterable to be shared across calls to {@link #emptyIterable()}. */ public static final FluentIterable EMPTY_ITERABLE = fromSupplier(Collections::emptyIterator); @VisibleForTesting J8Iterables() { } /** * Collect iterable of iterables into a mutable container. * * @param iterables The iterable of iterables * @param supplier The container supplier * @param accumulator The accumulator function * @param combiner The combiner function * @param <T> The type of elements * @param <R> The return type * @return Collected result * @see Stream#collect(Supplier, BiConsumer, BiConsumer) */ @Nullable public static <T, R> R collect(Iterable<Iterable<T>> iterables, Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner) { R result = supplier.get(); for (Iterable<T> iterable : iterables) { R innerResult = supplier.get(); for (T element : iterable) { accumulator.accept(innerResult, element); } combiner.accept(result, innerResult); } return result; } /** * Collect iterable into a mutable container. * * @param iterable The iterable * @param collector The collector object * @param <T> The type of elements * @param <A> The type of accumulator * @param <R> The return type * @return Collected result * @see Stream#collect(Collector) */ @Nullable public static <T, A, R> R collect(Iterable<T> iterable, Collector<? super T, A, R> collector) { A container = collector.supplier().get(); BiConsumer<A, ? super T> accumulator = collector.accumulator(); for (T t : iterable) { accumulator.accept(container, t); } return collector.finisher().apply(container); } /** * Perform a reduction on iterable. * * @param iterable The iterable * @param accumulator The accumulator function * @param <T> The type of elements * @return The reduced result * @see Stream#reduce(BinaryOperator) */ @Nullable public static <T> Optional<T> reduce(Iterable<T> iterable, BinaryOperator<T> accumulator) { boolean foundAny = false; T result = null; for (T element : iterable) { if (!foundAny) { foundAny = true; result = element; } else { result = accumulator.apply(result, element); } } return foundAny ? Optional.of(result) : Optional.empty(); } /** * Perform a reduction on an iterable. * * @param iterable The iterable * @param identity The identity to start reduction with * @param accumulator The accumulator function * @param <T> The type of elements * @return The reduced result * @see Stream#reduce(Object, BinaryOperator) */ @Nullable public static <T> T reduce(Iterable<T> iterable, @SuppressWarnings("SameParameterValue") @Nullable T identity, BinaryOperator<T> accumulator) { T result = identity; for (T element : iterable) { result = accumulator.apply(result, element); } return result; } /** * Perform a reduction on an iterable of iterables. * * @param iterables An iterator of iterables. * @param identity The identity to start reduction with * @param accumulator The accumulator function * @param combiner The combiner function * @param <T> The type of elements * @param <U> The reduced type * @return The reduced result * @see Stream#reduce(Object, BiFunction, BinaryOperator) */ @Nullable public static <T, U> U reduce(Iterable<Iterable<T>> iterables, @SuppressWarnings("SameParameterValue") @Nullable U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner) { U result = identity; for (Iterable<T> iterable : iterables) { U innerResult = identity; for (T element : iterable) { innerResult = accumulator.apply(innerResult, element); } result = combiner.apply(result, innerResult); } return result; } /** * Return the first and last elements or {@link Optional#empty()} if {@code Iterables.isEmpty(iterable)}. * * @param iterable The iterable to get the ends from * @param <T> The type of element in the iterable * @return optional {@link Ends} with the first and last of the iterable */ @Nonnull public static <T> Optional<Ends<T>> ends(Iterable<T> iterable) { return J8Iterators.ends(iterable.iterator()); } /** * Peek at the iterable without modifying the result. * * @param iterable The iterable to peek at * @param consumer The peeking function * @param <T> The type of elements * @return an Iterable that, when traversed will invoke the consumer on each element * @see Stream#peek(Consumer) */ @Nonnull public static <T> FluentIterable<T> peek(Iterable<T> iterable, Consumer<? super T> consumer) { return fromSupplier(() -> new PeekIterator<>(iterable.iterator(), consumer)); } /** * Return a peeking transformer - a UnaryOperator that will send each element to the consumer and return identity. * * @param consumer The peeking function * @param <T> The type of elements * @return a peeking (non-transforming) transformer */ @Nonnull public static <T> ConsumingIdentity<T> peeker(Consumer<T> consumer) { return new ConsumingIdentity<>(consumer); } /** * Create a one-time Iterable from a Stream. * * @param stream The Stream to use in creating an Iterable * @param <T> The type of elements * @return Iterable from the given stream */ @Nonnull public static <T> FluentIterable<T> fromStream(Stream<T> stream) { return new StreamIterable<>(stream); } /** * Create a {@link Stream} from the given {@link Iterable}. * * @param iterable The Iterable to use in creating a Stream * @param <T> The type of elements * @return Stream from the given iterable */ @Nonnull public static <T> Stream<T> toStream(Iterable<T> iterable) { if (iterable instanceof Collection) { return ((Collection<T>) iterable).stream(); } // TODO(scr): Is it possible to do late-binding (iterable::spliterator)? Need to know characteristics. return StreamSupport.stream(iterable.spliterator(), false); } /** * Create an {@link DoubleStream} from the given {@code doubleIterable}. * * @param doubleIterable The iterable to use in creating a DoubleStream * @return DoubleStream from the given iterable */ @Nonnull public static DoubleStream toStream(J8PrimitiveIterable.OfDouble doubleIterable) { // TODO(scr): Is it possible to do late-binding (iterable::spliterator)? Need to know characteristics. return StreamSupport.doubleStream(doubleIterable.primitiveSpliterator(), false); } /** * Create an {@link IntStream} from the given {@code intIterable}. * * @param intIterable The iterable to use in creating an IntStream * @return IntStream from the given iterable */ @Nonnull public static IntStream toStream(J8PrimitiveIterable.OfInt intIterable) { // TODO(scr): Is it possible to do late-binding (iterable::spliterator)? Need to know characteristics. return StreamSupport.intStream(intIterable.primitiveSpliterator(), false); } /** * Create a {@link LongStream} from the given {@code iterable}. * * @param longIterable The iterable to use in creating a LongStream * @return LongStream from the given iterable */ @Nonnull public static LongStream toStream(J8PrimitiveIterable.OfLong longIterable) { // TODO(scr): Is it possible to do late-binding (iterable::spliterator)? Need to know characteristics. return StreamSupport.longStream(longIterable.primitiveSpliterator(), false); } /** * Return a {@link FluentIterable} that is always empty. * * @param <T> The type of the FluentIterable * @return an empty FluentIterable */ @SuppressWarnings("unchecked") @Nonnull public static <T> FluentIterable<T> emptyIterable() { return (FluentIterable<T>) EMPTY_ITERABLE; } /** * Create a FluentIterable for elements. * <p> * Provides a wrapper to help where {@link FluentIterable} falls short - no varargs static constructor for testing. * * @param elements the elements to iterate over * @param <T> the type of elements * @return a FluentIterable for elements */ @SafeVarargs @Nonnull public static <T> FluentIterable<T> of(T... elements) { return FluentIterable.of(elements); } /** * Reverse the given {@code iterable}. * * @param iterable an iterable to reverse * @param <T> the type of elements * @return an iterable that reverses the navigableSet */ @Nonnull public static <T> FluentIterable<T> reverse(Iterable<? extends T> iterable) { // If it's already reversable, return it. if (iterable instanceof NavigableSet) { @SuppressWarnings("unchecked") NavigableSet<T> navigableSet = (NavigableSet<T>) iterable; return fromSupplier(navigableSet::descendingIterator); } else if (iterable instanceof Deque) { @SuppressWarnings("unchecked") Deque<T> deque = (Deque<T>) iterable; return fromSupplier(deque::descendingIterator); } else if (iterable instanceof List) { @SuppressWarnings("unchecked") List<T> list = (List<T>) iterable; return fromSupplier(() -> J8Iterators.reverse(list.listIterator(list.size()))); } // Slurp everything into a deque and then reverse its order. return fromSupplier(() -> { Deque<T> deque = new ArrayDeque<>(); Iterables.addAll(deque, iterable); return deque.descendingIterator(); }); } /** * Create a {@link FluentIterable} from the given {@link Supplier}. * * @param supplier the supplier * @param <T> the type of elements of the supplied iterable * @return an iterable */ @Nonnull public static <T> SupplierIterable<T> fromSupplier(Supplier<Iterator<? extends T>> supplier) { @SuppressWarnings("unchecked") Supplier<Iterator<T>> tSupplier = (Supplier<Iterator<T>>) (Supplier) supplier; return new SupplierIterable<>(tSupplier); } @Nonnull public static <T> J8PrimitiveIterable.OfDouble mapToDouble( Iterable<T> iterable, ToDoubleFunction<T> toDoubleFunction) { return new J8PrimitiveIterable.OfDouble() { @Override public PrimitiveIterator.OfDouble primitiveIterator() { return J8Iterators.mapToDouble(iterable.iterator(), toDoubleFunction); } @Override public Spliterator.OfDouble primitiveSpliterator() { return J8Spliterators.mapToDouble(iterable.spliterator(), toDoubleFunction); } }; } @Nonnull public static <T> J8PrimitiveIterable.OfInt mapToInt( Iterable<T> iterable, ToIntFunction<T> toIntFunction) { return new J8PrimitiveIterable.OfInt() { @Override public PrimitiveIterator.OfInt primitiveIterator() { return J8Iterators.mapToInt(iterable.iterator(), toIntFunction); } @Override public Spliterator.OfInt primitiveSpliterator() { return J8Spliterators.mapToInt(iterable.spliterator(), toIntFunction); } }; } @Nonnull public static <T> J8PrimitiveIterable.OfLong mapToLong( Iterable<T> iterable, ToLongFunction<T> toLongFunction) { return new J8PrimitiveIterable.OfLong() { @Override public PrimitiveIterator.OfLong primitiveIterator() { return J8Iterators.mapToLong(iterable.iterator(), toLongFunction); } @Override public Spliterator.OfLong primitiveSpliterator() { return J8Spliterators.mapToLong(iterable.spliterator(), toLongFunction); } }; } }