Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.bbn.bue.common.collections.MapUtils Maven / Gradle / Ivy
package com.bbn.bue.common.collections;
import com.bbn.bue.common.StringUtils;
import com.bbn.bue.common.collections.IterableUtils.ZipPair;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import static com.google.common.base.Preconditions.checkNotNull;
public final class MapUtils {
private MapUtils() {
throw new UnsupportedOperationException();
}
/**
* Pairs up the values of a map by their common keys.
*/
public static PairedMapValues zipValues(final Map left, final Map right) {
checkNotNull(left);
checkNotNull(right);
final ImmutableList.Builder> pairedValues =
ImmutableList.builder();
final ImmutableList.Builder leftOnly = ImmutableList.builder();
final ImmutableList.Builder rightOnly = ImmutableList.builder();
for (final Map.Entry leftEntry : left.entrySet()) {
final K key = leftEntry.getKey();
if (right.containsKey(key)) {
pairedValues.add(ZipPair.from(leftEntry.getValue(), right.get(key)));
} else {
leftOnly.add(leftEntry.getValue());
}
}
for (final Map.Entry rightEntry : right.entrySet()) {
if (!left.containsKey(rightEntry.getKey())) {
rightOnly.add(rightEntry.getValue());
}
}
return new PairedMapValues(pairedValues.build(), leftOnly.build(),
rightOnly.build());
}
/**
* Return a copy of the input map with keys transformed by {@code keyInjection} and values
* transformed by {@code valueFunction}. Beware: {@code keyInjection} must be an injection over
* all the keys of the input map. If two original keys are mapped to the same value, an {@link
* java.lang.IllegalArgumentException} will be returned.
*
* Neither {@code keyInjection} nor {@code valueFunction} may return null. If one does, an
* exception will be thrown.
*/
public static ImmutableMap copyWithTransformedEntries(Map input,
Function super K1, K2> keyInjection, Function super V1, V2> valueFunction) {
final ImmutableMap.Builder ret = ImmutableMap.builder();
for (final Map.Entry entry : input.entrySet()) {
ret.put(keyInjection.apply(entry.getKey()), valueFunction.apply(entry.getValue()));
}
return ret.build();
}
public static class PairedMapValues {
public PairedMapValues(final List> pairedValues, final List leftOnly,
final List rightOnly) {
this.pairedValues = ImmutableList.copyOf(pairedValues);
this.leftOnly = ImmutableList.copyOf(leftOnly);
this.rightOnly = ImmutableList.copyOf(rightOnly);
}
public List> pairedValues() {
return pairedValues;
}
public List leftOnly() {
return leftOnly;
}
public List rightOnly() {
return rightOnly;
}
public boolean perfectlyAligned() {
return leftOnly.isEmpty() && rightOnly.isEmpty();
}
private final List> pairedValues;
private final List leftOnly;
private final List rightOnly;
}
public static ImmutableSet allKeys(final Iterable extends Map> maps) {
final ImmutableSet.Builder builder = ImmutableSet.builder();
for (final Map map : maps) {
builder.addAll(map.keySet());
}
return builder.build();
}
public static Function, V> getEntryValue() {
return new Function, V>() {
@Override
public V apply(final Map.Entry entry) {
return entry.getValue();
}
};
}
public static Function, K> getEntryKey() {
return new Function, K>() {
@Override
public K apply(final Map.Entry entry) {
return entry.getKey();
}
};
}
public static Function, Iterable> getValues() {
return new Function, Iterable>() {
@Override
public Iterable apply(final Map input) {
return input.values();
}
};
}
public static > Ordering> byValueOrderingAscending() {
return Ordering.natural().onResultOf(MapUtils.getEntryValue());
}
public static > Ordering> byValueOrderingDescending() {
return Ordering.natural().onResultOf(MapUtils.getEntryValue()).reverse();
}
public static Ordering> byValueOrdering(final Ordering valueOrdering) {
return valueOrdering.onResultOf(MapUtils.getEntryValue());
}
public static List> sortedCopyOfEntries(final Map map,
final Ordering> ordering) {
return ordering.sortedCopy(map.entrySet());
}
/**
* Returns an immutable copy of the given map whose iteration order has keys sorted by the key
* type's natural ordering. No map keys or values may be {@code null}.
*/
public static , V> ImmutableMap copyWithSortedKeys(
final Map map) {
return copyWithKeysSortedBy(map, Ordering.natural());
}
/**
* Returns an immutable copy of the given map whose iteration order has keys sorted by the given
* {@link Ordering}. No map keys or values may be {@code null}.
*/
public static ImmutableMap copyWithKeysSortedBy(final Map map,
final Ordering super K> ordering) {
final ImmutableMap.Builder ret = ImmutableMap.builder();
for (final K key : ordering.sortedCopy(map.keySet())) {
ret.put(key, map.get(key));
}
return ret.build();
}
public static , V> Ordering> byKeyDescendingOrdering() {
return Ordering.natural().onResultOf(MapUtils.getEntryKey());
}
public static Ordering> byKeyOrdering(Ordering keyOrdering) {
return keyOrdering.onResultOf(MapUtils.getEntryKey());
}
/**
* Returns a new map which is contains all the mappings in both provided maps. This method will
* throw an exception if there is any key {@code K} such that {@code K} is a key in both maps and
* {@code !first.get(K).equals(second.get(K))}. The iteration order of the resulting map will
* contain first all entries in the first map in the order provided by {@code first.entrySet()}
* followed by all non-duplicate entries in the second map in the order provided by {@code
* second.entrySet()}. This method is null-intolerant: if either input map contains {@code null},
* an exception will be thrown.
*/
public static ImmutableMap disjointUnion(Map first, Map second) {
checkNotNull(first);
checkNotNull(second);
final ImmutableMap.Builder ret = ImmutableMap.builder();
// will throw an exception if there is any null mapping
ret.putAll(first);
for (final Entry secondEntry : second.entrySet()) {
final V firstForKey = first.get(secondEntry.getKey());
if (firstForKey == null) {
// non-duplicate
// we know the first map doesn't actually map this key to null
// or ret.putAll(first) above would fail
ret.put(secondEntry.getKey(), secondEntry.getValue());
} else if (firstForKey.equals(secondEntry.getValue())) {
// duplicate. This is okay. Do nothing.
} else {
throw new RuntimeException("When attempting a disjoint map union, " +
String.format("for key %s, first map had %s but second had %s", secondEntry.getKey(),
firstForKey, secondEntry.getValue()));
}
}
return ret.build();
}
/**
* Creates a copy of the supplied map with its keys transformed by the supplied function, which
* must be one-to-one on the keys of the original map. If two keys are mapped to the same value by
* {@code injection}, an {@code IllegalArgumentException} is thrown.
*/
public static ImmutableMap copyWithKeysTransformedByInjection(
final Map map, final Function super K1, K2> injection) {
final ImmutableMap.Builder ret = ImmutableMap.builder();
for (final Map.Entry entry : map.entrySet()) {
ret.put(injection.apply(entry.getKey()), entry.getValue());
}
return ret.build();
}
/**
* Returns the length of the longest key in a map, or 0 if the map is empty. Useful for printing
* tables, etc. The map may not have any null keys.
*/
public static int longestKeyLength(Map map) {
if (map.isEmpty()) {
return 0;
}
return Ordering.natural().max(
FluentIterable.from(map.keySet())
.transform(StringUtils.ToLength));
}
/**
* Just like {@link com.google.common.base.Functions#forMap(Map, Object)} except it allows a
* potentially non-constant {@link Function} as the default.
*/
public static Function asFunction(Map map,
Function defaultFunction) {
return new ForMapWithDefaultFunction(map, defaultFunction);
}
// indebted to Guava's Functions.forMap()
private static class ForMapWithDefaultFunction implements Function {
private final Map map;
private final Function defaultFunction;
private ForMapWithDefaultFunction(
final Map map, final Function defaultFunction) {
this.map = checkNotNull(map);
this.defaultFunction = checkNotNull(defaultFunction);
}
@Override
public V apply(final K input) {
final V result = map.get(input);
return (result != null || map.containsKey(result)) ? result : defaultFunction.apply(input);
}
@Override
public int hashCode() {
return Objects.hashCode(map, defaultFunction);
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final ForMapWithDefaultFunction other = (ForMapWithDefaultFunction) obj;
return Objects.equal(this.map, other.map)
&& Objects.equal(this.defaultFunction, other.defaultFunction);
}
@Override
public String toString() {
return "asFunction(" + map + ", default=" + defaultFunction + ")";
}
}
/**
* Creates a {@link ImmutableMap} by pairing up a sequence of keys and values. The {@code n}-th
* key is paired to the {@code n}-th value. Neither null keys nor null values are permitted. The
* keys must be unique or an {@link IllegalArgumentException} will be thrown. If the numberof
* elements returned by the two {@link Iterable}s differ, an {@link IllegalArgumentException} will
* be thrown.
*/
public static ImmutableMap copyParallelListsToMap(Iterable keys,
Iterable values) {
final ImmutableMap.Builder ret = ImmutableMap.builder();
final Iterator keyIt = keys.iterator();
final Iterator valueIt = values.iterator();
while (keyIt.hasNext() && valueIt.hasNext()) {
ret.put(keyIt.next(), valueIt.next());
}
if (!keyIt.hasNext() && !valueIt.hasNext()) {
return ret.build();
} else {
if (keyIt.hasNext()) {
throw new IllegalArgumentException(
"When pairing keys and values, there were more keys than values");
} else {
throw new IllegalArgumentException(
"When pairing keys and values, there were more values than keys");
}
}
}
/**
* Creates a {@code LaxImmutableMapBuilder} which will behave exactly like a
* {@link com.google.common.collect.ImmutableMap.Builder} except it will allow adding the
* same key-value pair more than once. "Same" is determined by the value's equality method.
* The behavior of this builder and the resulting {@link ImmutableMap} is deterministic.
*/
public static LaxImmutableMapBuilder immutableMapBuilderAllowingSameEntryTwice() {
return new MonotonicLaxImmutableMapBuilder<>(false);
}
/**
* Creates a {@code LaxImmutableMapBuilder} which will behave exactly like a
* {@link com.google.common.collect.ImmutableMap.Builder} except it will silently ignore
* any attempt to add a new entry with a previously seen key.
* The behavior of this builder and the resulting {@link ImmutableMap} is deterministic.
*/
public static LaxImmutableMapBuilder immutableMapBuilderIgnoringDuplicates() {
return new MonotonicLaxImmutableMapBuilder<>(true);
}
/**
* Creates a {@code LaxImmutableMapBuilder} which will behave exactly like a
* {@link com.google.common.collect.ImmutableMap.Builder} except that when attempting to add
* a value for a previously seen key, the greater of the older value and the new value according
* to the provided comparator will be used. In case of a tie, the incumbent stays.
*
* The behavior of this builder and the resulting {@link ImmutableMap} will be deterministic if
* the supplied comparator is.
*/
public static LaxImmutableMapBuilder immutableMapBuilderResolvingDuplicatesBy(Comparator super V> conflictComparator) {
return new NonMonotonicLaxImmutableMapBuilder<>(Ordering.from(conflictComparator));
}
/**
* Returns a Guava {@link Function} to transform map entries to strings by taking the {@link
* Object#toString()} of the key and the value and joining them with the supplied {@code
* separator}. None of the key, value, or separator may be null.
*/
public static Function, String> toStringWithKeyValueSeparator(
final String separator) {
return new EntryJoinerFunction<>(separator);
}
private static class EntryJoinerFunction implements Function, String> {
private final String separator;
public EntryJoinerFunction(final String separator) {
this.separator = checkNotNull(separator);
}
@Override
public String apply(final Entry input) {
return input.getKey() + separator + input.getValue();
}
@Override
public int hashCode() {
return Objects.hashCode(separator);
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final EntryJoinerFunction other = (EntryJoinerFunction) obj;
return Objects.equal(this.separator, other.separator);
}
}
/**
* Just like {@link Maps#transformEntries(Map, Maps.EntryTransformer)} but on an {@link Iterable}
* of {@link Map.Entry}.
*/
public static Iterable> transformValues(
Iterable> input,
final Function super V1, V2> valueTransformer) {
return Iterables.transform(input, new Function, Entry>() {
@Override
public Entry apply(final Entry input) {
return Maps.immutableEntry(input.getKey(), valueTransformer.apply(input.getValue()));
}
});
}
/**
* Just like {@link Maps#transformEntries(Map, Maps.EntryTransformer)} but on an {@link Iterable}
* of {@link Map.Entry}.
*/
public static Iterable> transformEntries(
Iterable> input,
final Maps.EntryTransformer super K, ? super V1, V2> entryTransformer) {
return Iterables.transform(input, new Function, Entry>() {
@Override
public Entry apply(final Entry input) {
return Maps.immutableEntry(input.getKey(),
entryTransformer.transformEntry(input.getKey(), input.getValue()));
}
});
}
/**
* Creates a {@link com.google.common.collect.Maps.EntryTransformer} which applies the supplied
* {@link Function} to map entry values.
*/
public static Maps.EntryTransformer valueTransformer(
final Function super V1, V2> valueFunction) {
checkNotNull(valueFunction);
return new Maps.EntryTransformer() {
@Override
public V2 transformEntry(final K key, final V1 value) {
return valueFunction.apply(value);
}
};
}
/**
* Returns a wrapper around an {@link com.google.common.collect.ImmutableMultimap.Builder} which
* hides the differences between it and {@link com.google.common.collect.ImmutableMap.Builder} for
* the purpse of population.
*/
public static KeyValueSink asMapSink(ImmutableMultimap.Builder multimapB) {
return new ImmutableMultimapBuilderSink<>(multimapB);
}
/**
* Returns a wrapper around an {@link com.google.common.collect.ImmutableMap.Builder} which
* hides the differences between it and {@link com.google.common.collect.ImmutableMultimap.Builder} for
* the purpse of population. Beware {@link com.google.common.collect.ImmutableMap.Builder} will
* still throw an exception for duplicate entries!
*/
public static KeyValueSink asMapSink(ImmutableMap.Builder mapB) {
return new ImmutableMapBuilderSink<>(mapB);
}
}
abstract class AbstractKeyValueSink implements KeyValueSink {
@Override
public final KeyValueSink put(final Entry entry) {
put(entry.getKey(), entry.getValue());
return this;
}
@Override
public final KeyValueSink putAll(
final Iterable extends Entry extends K, ? extends V>> entries) {
for (final Entry extends K, ? extends V> entry : entries) {
put(entry.getKey(), entry.getValue());
}
return this;
}
}
final class ImmutableMapBuilderSink extends AbstractKeyValueSink {
final ImmutableMap.Builder builder;
ImmutableMapBuilderSink(final ImmutableMap.Builder builder) {
this.builder = checkNotNull(builder);
}
@Override
public KeyValueSink put(final K key, final V value) {
builder.put(key, value);
return this;
}
}
final class ImmutableMultimapBuilderSink extends AbstractKeyValueSink {
final ImmutableMultimap.Builder builder;
ImmutableMultimapBuilderSink(final ImmutableMultimap.Builder builder) {
this.builder = checkNotNull(builder);
}
@Override
public KeyValueSink put(final K key, final V value) {
builder.put(key, value);
return this;
}
}