edu.isi.nlp.collections.LaxImmutableMapBuilder Maven / Gradle / Ivy
package edu.isi.nlp.collections;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import java.util.Comparator;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Guava's {@link com.google.common.collect.ImmutableMap.Builder} disallows adding the same key
* twice, even with the same value. In many situations this is inconvenient, so this interface is
* the parent of builders which relax this constraint in various ways. See {@link
* MapUtils#immutableMapBuilderAllowingSameEntryTwice()}, {@link
* MapUtils#immutableMapBuilderIgnoringDuplicates()}, and {@link
* MapUtils#immutableMapBuilderResolvingDuplicatesBy(Comparator)}.
*
* You might think that this is unnecessary because you can simply build your map using e.g. a
* {@link java.util.HashMap} and then call {@link ImmutableMap#copyOf(Iterable)}, but that loses
* {@link ImmutableMap.Builder}'s very nice determinism guarantees.
*
*
These builders are generally less efficient than {@link ImmutableMap.Builder}, so be cautious
* using them in performance-sensitive code.
*/
public interface LaxImmutableMapBuilder {
LaxImmutableMapBuilder put(K key, V value);
LaxImmutableMapBuilder putAll(Map extends K, ? extends V> map);
LaxImmutableMapBuilder putAll(
Iterable extends Map.Entry extends K, ? extends V>> entries);
/** See {@link ImmutableMap.Builder#orderEntriesByValue(Comparator)} */
LaxImmutableMapBuilder orderEntriesByValue(Comparator super V> valueComparator);
ImmutableMap build();
}
/** Implementation for {@link LaxImmutableMapBuilder}s which never need to change their mind. */
final class MonotonicLaxImmutableMapBuilder implements LaxImmutableMapBuilder {
private final Map mappingsSeen = Maps.newHashMap();
private final ImmutableMap.Builder innerBuilder = ImmutableMap.builder();
private final boolean keepFirst;
MonotonicLaxImmutableMapBuilder(final boolean keepFirst) {
this.keepFirst = keepFirst;
}
@Override
public LaxImmutableMapBuilder put(K key, V value) {
checkNotNull(key);
checkNotNull(value);
final V existingMapping = mappingsSeen.get(key);
if (null == existingMapping) {
mappingsSeen.put(key, value);
innerBuilder.put(key, value);
} else if (existingMapping.equals(value)) {
// do nothing
} else if (keepFirst) {
// do nothing, we have been requested to silently keep the first value
} else {
throw new IllegalArgumentException(
"Refusing to add two mappings for the key "
+ key
+ ": "
+ existingMapping
+ " and "
+ value);
}
return this;
}
@Override
public LaxImmutableMapBuilder putAll(Map extends K, ? extends V> map) {
for (final Map.Entry extends K, ? extends V> e : map.entrySet()) {
put(e.getKey(), e.getValue());
}
return this;
}
@Override
public LaxImmutableMapBuilder putAll(
Iterable extends Map.Entry extends K, ? extends V>> entries) {
for (final Map.Entry extends K, ? extends V> e : entries) {
put(e.getKey(), e.getValue());
}
return this;
}
/** See {@link ImmutableMap.Builder#orderEntriesByValue(Comparator)} */
@Override
public LaxImmutableMapBuilder orderEntriesByValue(Comparator super V> valueComparator) {
innerBuilder.orderEntriesByValue(valueComparator);
return this;
}
@Override
public ImmutableMap build() {
return innerBuilder.build();
}
}
/** Implementation for {@link LaxImmutableMapBuilder}s which do need to change their mind. */
final class NonMonotonicLaxImmutableMapBuilder implements LaxImmutableMapBuilder {
private final ImmutableSet.Builder orderKeysSeenIn = ImmutableSet.builder();
private final Map mappingsSeen = Maps.newHashMap();
private final Ordering super V> conflictComparator;
private @Nullable Comparator super V> immutableMapEntryOrdering = null;
NonMonotonicLaxImmutableMapBuilder(final Ordering super V> conflictComparator) {
this.conflictComparator = checkNotNull(conflictComparator);
}
@Override
public LaxImmutableMapBuilder put(K key, V value) {
checkNotNull(key);
checkNotNull(value);
final V existingMapping = mappingsSeen.get(key);
if (null == existingMapping) {
mappingsSeen.put(key, value);
orderKeysSeenIn.add(key);
} else if (existingMapping.equals(value)) {
// do nothing
} else {
// add only if new value is larger by the provided comparator
if (conflictComparator.max(existingMapping, value).equals(value)) {
mappingsSeen.put(key, value);
}
}
return this;
}
@Override
public LaxImmutableMapBuilder putAll(Map extends K, ? extends V> map) {
for (final Map.Entry extends K, ? extends V> e : map.entrySet()) {
put(e.getKey(), e.getValue());
}
return this;
}
@Override
public LaxImmutableMapBuilder putAll(
Iterable extends Map.Entry extends K, ? extends V>> entries) {
for (final Map.Entry extends K, ? extends V> e : entries) {
put(e.getKey(), e.getValue());
}
return this;
}
/** See {@link ImmutableMap.Builder#orderEntriesByValue(Comparator)} */
@Override
public LaxImmutableMapBuilder orderEntriesByValue(Comparator super V> valueComparator) {
this.immutableMapEntryOrdering = checkNotNull(valueComparator);
return this;
}
@Override
public ImmutableMap build() {
final ImmutableMap.Builder ret = ImmutableMap.builder();
if (immutableMapEntryOrdering != null) {
ret.orderEntriesByValue(immutableMapEntryOrdering);
}
for (final K key : orderKeysSeenIn.build()) {
ret.put(key, mappingsSeen.get(key));
}
return ret.build();
}
}