ProjectAres/Util/core/src/main/java/tc/oc/commons/core/IterableUtils.java

328 lines
12 KiB
Java

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());
}
}