Skip to content

Instantly share code, notes, and snippets.

@dominicthomas
Created April 15, 2015 14:09
Show Gist options
  • Save dominicthomas/9530f720519b074ec945 to your computer and use it in GitHub Desktop.
Save dominicthomas/9530f720519b074ec945 to your computer and use it in GitHub Desktop.
Stuff Guava is Missing
import static com.google.common.base.Optional.of;
import static com.google.common.collect.Iterables.concat;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.isEmpty;
import static com.google.common.collect.Iterables.transform;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
import edu.umd.cs.findbugs.annotations.NonNull;
/**
* A Bunch of generic collection stuff that really should have been in Guava.
*/
public class CollectionUtil {
private CollectionUtil() {
}
/**
* Flattens a list of lists into a single list.
* Like flatMap in Scala or bind in Haskell, but restricted to Iterables only thanks to Java's laughable type system.
*/
public static <A, B> Iterable<B> flatTransform(Iterable<A> i, Function<? super A, ? extends Iterable<B>> f) {
return concat(transform(i, f));
}
/**
* Groups elements into a list of lists, with grouping determined by the given function.
*
* @param i the elements to group
* @param f determines the property to group by
* @return A list of grouped elements.
*/
public static <A, B> Collection<? extends Collection<? extends A>> group(Iterable<? extends A> i,
Function<A, B> f) {
return Multimaps.index(i, f).asMap().values();
}
/**
* Left Fold.
* (a, b, c, d), initial -> f(f(f(f(initial,a), b), c), d)
* http://en.wikipedia.org/wiki/Fold_(higher-order_function)
*/
public static <A, B> A fold(final Iterable<? extends B> gen, final A initial,
final Function2<? super A, ? super B, ? extends A> function) {
final Iterator<? extends B> it = gen.iterator();
if (!it.hasNext()) {
return initial;
}
A acc = initial;
while (it.hasNext()) {
acc = function.apply(acc, it.next());
}
return acc;
}
/**
* A transformation function of arity-2 (two arguments) from A and B to C.
*/
public interface Function2<A, B, C> {
C apply(A a, B b);
}
/**
* Sums Integers.
* @param ints a collection of {@code Integer} objects
* @return sum of ints
* @throws NullPointerException if {@code ints} or any of its elements is null
*/
public static int sum(@NonNull Iterable<Integer> ints) {
int sum = 0;
for (int anInt : ints) {
sum += anInt;
}
return sum;
}
/**
* Returns true if all {@code B}s are equal
*/
public static <A, B> boolean allEqual(Iterable<A> iterable, Function<A, B> function) {
return allEqual(transform(iterable, function));
}
/**
* Returns true if all elements in the List are equal
*/
public static boolean allEqual(Iterable<?> iterable) {
return Sets.newHashSet(iterable).size() < 2;
}
/**
* Returns the count of unique {@code A}s according to supplied comparator
*/
public static <A> int countDistinct(Iterable<A> iterable, Comparator<? super A> comparator) {
return ImmutableSortedSet.copyOf(comparator, iterable).size();
}
/**
* Returns a list with duplicates removed according to the given comparator.
*/
public static <A> Iterable<A> distinct(Iterable<A> iterable, Comparator<? super A> comparator) {
return ImmutableSortedSet.copyOf(comparator, iterable);
}
/**
* Determines if the given iterable contains no elements.
*/
public static boolean isNotEmpty(Iterable<?> iterable) {
return !isEmpty(iterable);
}
/**
* Returns true if the predicate holds for at least one of the elements of this iterable, false otherwise
* (false for the empty list).
*/
public static <A> boolean exists(Iterable<A> iterable, Predicate<A> predicate) {
return isNotEmpty(filter(iterable, predicate));
}
/**
* Returns true if the iterable has at least one element whose class is {@code type} or a subclass of {@code type}.
*/
public static boolean exists(Iterable<?> iterable, Class<?> type) {
return isNotEmpty(filter(iterable, type));
}
/**
* Syntactic sugar for Guava's Predicates.in()
*/
public static <T> Predicate<T> in(T... target) {
return Predicates.in(Lists.newArrayList(target));
}
/**
* Simply returns a new Iterable with each element wrapped in an Optional.
*/
public static <A> Iterable<Optional<A>> asOptionalList(Iterable<A> iterable) {
Collection<Optional<A>> list = new ArrayList<Optional<A>>();
for (A a : iterable) {
list.add(of(a));
}
return list;
}
/**
* Creates a Guava multimap using the input map.
*/
public static <K, V> Multimap<K, V> createMultiMap(Map<K, ? extends Iterable<V>> input) {
Multimap<K, V> multimap = ArrayListMultimap.create();
for (Map.Entry<K, ? extends Iterable<V>> entry : input.entrySet()) {
multimap.putAll(entry.getKey(), entry.getValue());
}
return multimap;
}
/**
* Creates an Immutable Guava multimap using the input map.
*/
public static <K, V> ImmutableMultimap<K, V> createImmutableMultiMap(Map<K, ? extends Iterable<V>> input) {
ImmutableMultimap.Builder<K, V> builder = ImmutableMultimap.builder();
for (Map.Entry<K, ? extends Iterable<V>> entry : input.entrySet()) {
builder.putAll(entry.getKey(), entry.getValue());
}
return builder.build();
}
public static <K, V> Map<K, Collection<V>> combine(Map<K, Collection<V>> map1, Map<K, Collection<V>> map2) {
return combine(createMultiMap(map1), createMultiMap(map2)).asMap();
}
public static <K, V> Multimap<K, V> combine(Multimap<K, V> map1, Multimap<K, V> map2) {
ArrayListMultimap<K, V> map = ArrayListMultimap.create();
map.putAll(map1);
map.putAll(map2);
return map;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment