package tc.oc.commons.core; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Random; import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import javax.annotation.Nullable; import com.google.common.base.Preconditions; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultiset; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Multiset; import com.google.common.collect.Sets; import com.google.common.reflect.TypeToken; import tc.oc.commons.core.reflect.Methods; import tc.oc.commons.core.util.Comparators; import tc.oc.commons.core.util.IteratorUtils; import tc.oc.commons.core.util.Streams; import tc.oc.commons.core.util.ThrowingConsumer; /** {@link Iterable}-related utilities. */ public class IterableUtils { private static final Method ITERATOR_METHOD = Methods.method(Iterable.class, "iterator"); public static <T> TypeToken<? extends Iterator<T>> iteratorType(TypeToken<? extends Iterable<T>> iterableType) { return (TypeToken<? extends Iterator<T>>) iterableType.method(ITERATOR_METHOD).getReturnType(); } public static <T> TypeToken<T> elementType(TypeToken<? extends Iterable<T>> iterableType) { return IteratorUtils.elementType(iteratorType(iterableType)); } /** * Finds the most common element in the specified {@link Iterable}. If there is a tie and <code>tie</code> is true, * a random selection (from the tied elements) is made. If there is a tie and <code>tie</code> is not true, * <code>null</code> is returned, instead. * * @param iterable The {@link Iterable} to check. * @param <T> The type of the {@link Iterable}. * @return The most common element, an arbitrary selection from the tied elements if a tie arises and * <code>tie</code> is true, or <code>null</code> if a tie arises and <code>tie</code> is not true.. */ public static @Nullable <T> T findMostCommon(Iterable<T> iterable, boolean tie) { HashMap<T, Integer> counts = new HashMap<>(); for (T obj : Preconditions.checkNotNull(iterable, "Iterable")) { Integer count = counts.get(obj); counts.put(obj, count == null ? 1 : count + 1); } int max = 0; T maxObj = null; for (Map.Entry<T, Integer> entry : counts.entrySet()) { int value = entry.getValue(); if (maxObj == null || value >= max) { max = value; maxObj = entry.getKey(); } } if (!tie && maxObj != null) { for (Map.Entry<T, Integer> entry : counts.entrySet()) { if (entry.getValue() == max && !maxObj.equals(entry.getKey())) { return null; } } } return maxObj; } /** * Finds the most common element in the specified {@link Iterable}. If there is a tie, a random selection (from the * tied elements) is made. If this functionality is undesired, {@link #findMostCommon(Iterable, boolean)} may be * used instead. * * @param iterable The {@link Iterable} to check. * @param <T> The type of the {@link Iterable}. * @return The most common element, or an arbitrary selection from the tied elements if a tie arises. */ public static <T> T findMostCommon(Iterable<T> iterable) { return findMostCommon(iterable, true); } /** * Transform and filter at the same time. * Return null from the transform function to skip the current element. */ public static <In, Out> Iterator<Out> transfilter(final Iterator<In> iterator, final Function<? super In, ? extends Out> function) { return new Iterator<Out>() { Out next; @Override public boolean hasNext() { if(next != null) { return true; } else { while(iterator.hasNext()) { next = function.apply(iterator.next()); if(next != null) return true; } return false; } } @Override public Out next() { if(!hasNext()) throw new NoSuchElementException(); Out tmp = next; next = null; return tmp; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } /** * Transform and filter at the same time. * Return null from the transform function to skip the current element. */ public static <In, Out> Iterable<Out> transfilter(final Iterable<In> iterable, final Function<? super In, ? extends Out> function) { return new Iterable<Out>() { @Override public Iterator<Out> iterator() { return transfilter(iterable.iterator(), function) ; } }; } public static <In, Out> Collection<Out> transfilter(final Collection<In> collection, final Function<? super In, ? extends Out> function) { return ImmutableList.copyOf(transfilter((Iterable) collection, function)); } public static Iterable<String> toStrings(Iterable<?> things) { return Iterables.transform(things, String::valueOf); } public static Collection<String> toStrings(Collection<?> things) { return Collections2.transform(things, String::valueOf); } /** * Return a copy of the given collection in whatever subclass of {@link ImmutableCollection} fits best */ public static <E> ImmutableCollection<E> immutableCopyOf(Collection<E> things) { if(things instanceof List) { return ImmutableList.copyOf(things); } else { return ImmutableSet.copyOf(things); } } public static <T> Iterable<T> prepend(T element, Iterable<? extends T> iterable) { return Iterables.concat(ImmutableSet.of(element), iterable); } public static <T> Iterable<T> append(Iterable<? extends T> iterable, T...rest) { return Iterables.concat(iterable, Arrays.asList(rest)); } public static <T> void reverseForEach(Iterable<T> iterable, Consumer<? super T> consumer) { Streams.reverseStream(iterable).forEach(consumer); } public static <T, E extends Throwable> void reverseForEachThrows(Iterable<T> iterable, ThrowingConsumer<? super T, E> consumer) throws E { Streams.forEachThrows(Streams.reverseStream(iterable), consumer); } public static <T> Set<T> instancesOf(Set<? super T> set, Class<T> type) { return (Set<T>) Sets.filter(set, type::isInstance); } public static <T> boolean any(Iterable<T> iterable, Predicate<? super T> predicate) { for(T t : iterable) { if(predicate.test(t)) return true; } return false; } public static <T> boolean all(Iterable<T> iterable, Predicate<? super T> predicate) { for(T t : iterable) { if(!predicate.test(t)) return false; } return true; } public static <T> boolean none(Iterable<T> iterable, Predicate<? super T> predicate) { for(T t : iterable) { if(predicate.test(t)) return false; } return true; } public static <T> Iterable<T> emptyIterable() { return EMPTY_ITERABLE; } private static final Iterable EMPTY_ITERABLE = Collections::emptyIterator; /** * Return the first element in the given (non-empty) {@link Iterable} * @throws NoSuchElementException if the iterable is empty */ public static <T> T getFirst(Iterable<? extends T> iterable) { if(iterable instanceof List) { final List<T> list = (List<T>) iterable; if(list.isEmpty()) throw new NoSuchElementException(); return list.get(0); } else { return iterable.iterator().next(); } } /** * Return the concatenation of the {@link Iterables} in the given {@link Collection}. * * Result is equivalent to {@link Iterables#concat}, but there are some optimizations * for the cases where size == 0 and size == 1. */ public static <T> Iterable<T> concat(Collection<? extends Iterable<? extends T>> iterables) { switch(iterables.size()) { case 0: return emptyIterable(); case 1: return (Iterable<T>) getFirst(iterables); default: return Iterables.concat(iterables); } } @SafeVarargs public static <T> Iterable<T> concat(Iterable<? extends T>... iterables) { switch(iterables.length) { case 0: return emptyIterable(); case 1: return (Iterable<T>) iterables[0]; default: return Iterables.concat(iterables); } } @SafeVarargs public static <T> Iterable<T> unique(Iterable<? extends T>... iterables) { return ImmutableSet.copyOf(concat(iterables)); } public static <T> List<T> asList(Iterable<? extends T> iterable) { if(iterable instanceof List) { return (List<T>) iterable; } else { return ImmutableList.copyOf(iterable); } } public static <T> T randomElement(Iterable<? extends T> iterable, Random random) { return ListUtils.randomElement(asList(iterable), random); } public static <T> ImmutableCollection<T> copyOf(Iterable<T> iterable) { if(iterable instanceof Set) { return ImmutableSet.copyOf(iterable); } else if(iterable instanceof Multiset) { return ImmutableMultiset.copyOf(iterable); } else { return ImmutableList.copyOf(iterable); } } public static <T> T removeNext(Iterator<T> iterator) { final T t = iterator.next(); iterator.remove(); return t; } public static <T> T removeFirst(Iterable<T> iterable) { return removeNext(iterable.iterator()); } public static <T> T unify(Iterable<? extends T> things, T empty, Function<Iterable<? extends T>, ? extends T> unifier) { if(things instanceof Collection) { final Collection<? extends T> collection = (Collection<? extends T>) things; switch(collection.size()) { case 0: return empty; case 1: return collection.iterator().next(); default: return unifier.apply(things); } } else { final Iterator<? extends T> iterator = things.iterator(); if(!iterator.hasNext()) return empty; final T thing = iterator.next(); if(!iterator.hasNext()) return thing; return unifier.apply(things); } } public static <T> Iterable<T> sorted(Iterable<T> unsorted, Comparator<? super T> order) { final List<T> sorted = Lists.newArrayList(unsorted); sorted.sort(order); return sorted; } public static <T extends Comparable<? super T>> Iterable<T> ascending(Iterable<T> unsorted) { return sorted(unsorted, Comparator.naturalOrder()); } public static <T extends Comparable<? super T>> Iterable<T> descending(Iterable<T> unsorted) { return sorted(unsorted, Comparator.reverseOrder()); } }