edu.isi.nlp.collections.MultimapUtils Maven / Gradle / Ivy
package edu.isi.nlp.collections;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import java.util.Collection;
import java.util.Map;
/**
* Utilities for working with {@link Multimap}s.
*
* @author Jay DeYoung, Ryan Gabbard
*/
public final class MultimapUtils {
private MultimapUtils() {
throw new UnsupportedOperationException();
}
/**
* This will take a multimap which in fact has a single value map for each key and return a copy
* of it as an {@link com.google.common.collect.ImmutableMap}. If the same key if mapped to
* multiple values in the input multimap, an {@link java.lang.IllegalArgumentException} is thrown,
* so this should generally not be used on multimaps built from user input unless this property is
* guaranteed to hold. This is mostly useful for statically building maps which are more naturally
* represented as inverted multimaps.
*
* Preserves iteration order.
*/
public static Map copyAsMap(Multimap multimap) {
final Map> inputAsMap = multimap.asMap();
final ImmutableMap.Builder ret = ImmutableMap.builder();
for (final Map.Entry> mapping : inputAsMap.entrySet()) {
ret.put(mapping.getKey(), Iterables.getOnlyElement(mapping.getValue()));
}
return ret.build();
}
/**
* Converts a {@link com.google.common.collect.Multimap} to a {@link java.util.Map} by selecting a
* single value for each key and discarding the others, with the selection procedure being defined
* by {@code reducerFunction}, which may not return {@code null}. Additionally, {@code multimap}
* may not have {@code null} keys.
*/
public static ImmutableMap reduceToMap(
Multimap multimap, Function super Collection, ? extends V> reducerFunction) {
final ImmutableMap.Builder ret = ImmutableMap.builder();
for (final Map.Entry> entry : multimap.asMap().entrySet()) {
// nulls banned by contract
//noinspection ConstantConditions
ret.put(entry.getKey(), reducerFunction.apply(entry.getValue()));
}
return ret.build();
}
/**
* Returns an {@link ImmutableSet} of all items in the multimap for the given set of keys, without
* preserving mulitiplicity.
*/
public static ImmutableSet getAllAsSet(
final Multimap multimap, final Iterable keys) {
final ImmutableSet.Builder ret = ImmutableSet.builder();
for (final K key : keys) {
ret.addAll(multimap.get(key));
}
return ret.build();
}
/**
* Performs a (non-strict) composition of two multimaps to an {@link ImmutableSetMultimap}. This
* returns a new {@link ImmutableSetMultimap} which will contain an entry {@code (k,v)} if and
* only if there is some {@code i} such that {@code (k, i)} is in {@code first} and {@code (i,v)}
* is in {@code second}. Neither {@code first} nor {@code second} is permitted to contain null
* keys or values. The output of this method is not a view and will not be updated for changes to
* the input multimaps.
*
* This method will allow {@code first} to contain values not found as keys of {@code second}.
* If you wish to disallow this, see {@link #composeToSetMultimapStrictly(Multimap, Multimap)}.
*/
public static ImmutableSetMultimap composeToSetMultimap(
final Multimap first, final Multimap second) {
final ImmutableSetMultimap.Builder result = ImmutableSetMultimap.builder();
for (K1 k1 : first.keySet()) {
checkNotNull(k1);
for (V1 v1 : first.get(k1)) {
checkNotNull(v1);
result.putAll(k1, second.get(v1));
}
}
return result.build();
}
/**
* Performs a (strict) composition of two multimaps to an {@link ImmutableSetMultimap}. This
* returns a new {@link ImmutableSetMultimap} which will contain an entry {@code (k,v)} if and
* only if there is some {@code i} such that {@code (k, i)} is in {@code first} and {@code (i, v)}
* is in {@code second}. Neither {@code first} nor {@code second} is permitted to contain null
* keys or values. The output of this method is not a view and will not be updated for changes to
* the input multimaps. Strict compositions require that for each entry {@code (k,i)} in {@code
* first}, there exists an entry {@code (i,v)} in {@code second}.
*/
public static
ImmutableSetMultimap composeToSetMultimapStrictly(
final Multimap first, final Multimap second) {
checkArgument(second.keySet().containsAll(first.values()));
return composeToSetMultimap(first, second);
}
/**
* Creates a copy of the supplied multimap with its keys transformed by the supplied function.
*
* The {@code injection} must never return null and the input multimap must contain no nulls.
*/
public static ImmutableListMultimap copyWithTransformedKeys(
final ListMultimap listMultimap, final Function super K1, ? extends K2> injection) {
final ImmutableListMultimap.Builder ret = ImmutableListMultimap.builder();
for (final Map.Entry entry : listMultimap.entries()) {
// nulls banned by contract
//noinspection ConstantConditions
ret.put(injection.apply(entry.getKey()), entry.getValue());
}
return ret.build();
}
/**
* Creates a copy of the supplied multimap with its keys transformed by the supplied function.
*
* The {@code function} must never return null and the input multimap must contain no nulls.
*/
public static ImmutableSetMultimap copyWithTransformedKeys(
final SetMultimap setMultimap, final Function super K1, ? extends K2> function) {
final ImmutableSetMultimap.Builder ret = ImmutableSetMultimap.builder();
for (final Map.Entry entry : setMultimap.entries()) {
// nulls banned by contract
//noinspection ConstantConditions
ret.put(function.apply(entry.getKey()), entry.getValue());
}
return ret.build();
}
/**
* Exactly like {@link Multimaps#index(Iterable, Function)}, except the key function can provide
* multiple keys for a given value. The {@code keyFunction} may never return {@code null} or a
* collection containing {@code null}.
*/
public static ImmutableSetMultimap indexToSetMultimapWithMultipleKeys(
Iterable extends V> values,
Function super V, ? extends Collection extends K>> keyFunction) {
final ImmutableSetMultimap.Builder ret = ImmutableSetMultimap.builder();
for (final V value : values) {
// nulls banned by contract
//noinspection ConstantConditions
for (K key : keyFunction.apply(value)) {
ret.put(key, value);
}
}
return ret.build();
}
/**
* Exactly like {@link Multimaps#index(Iterable, Function)}, except the key function can provide
* multiple keys for a given value. The {@code keyFunction} may never return {@code null} or a
* collection containing {@code null}.
*/
public static ImmutableListMultimap indexToListMultimapWithMultipleKeys(
Iterable extends V> values,
Function super V, ? extends Collection extends K>> keyFunction) {
final ImmutableListMultimap.Builder ret = ImmutableListMultimap.builder();
for (final V value : values) {
// nulls banned by contract
//noinspection ConstantConditions
for (K key : keyFunction.apply(value)) {
ret.put(key, value);
}
}
return ret.build();
}
}