// =================================================================================================
// Copyright 2011 Twitter, Inc.
// -------------------------------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this work except in compliance with the License.
// You may obtain a copy of the License in the LICENSE file, or at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =================================================================================================
package com.twitter.common.collections;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
/**
* Utility class for functions related to Multimaps in the java collections library.
*
* @author William Farner
*/
public final class Multimaps {
private Multimaps() {
// Utility.
}
/**
* Prunes a multimap based on a predicate, returning the pruned values. The input map will be
* modified.
*
* @param map The multimap to prune.
* @param filterRule The pruning rule. When the predicate returns {@code false} for an entry, it
* will be pruned, otherwise it will be retained.
* @param <K> The key type in the multimap.
* @param <V> The value type in the multimap.
* @return A new multimap, containing the pruned keys/values.
*/
public static <K, V> Multimap<K, V> prune(Multimap<K, V> map,
Predicate<? super Collection<V>> filterRule) {
Preconditions.checkNotNull(map);
Preconditions.checkNotNull(filterRule);
Multimap<K, V> pruned = ArrayListMultimap.create();
Iterator<Map.Entry<K, Collection<V>>> asMapItr = map.asMap().entrySet().iterator();
while (asMapItr.hasNext()) {
Map.Entry<K, Collection<V>> asMapEntry = asMapItr.next();
if (!filterRule.apply(asMapEntry.getValue())) {
pruned.putAll(asMapEntry.getKey(), asMapEntry.getValue());
asMapItr.remove();
}
}
return pruned;
}
private static final class AtLeastSize implements Predicate<Collection<?>> {
private final int minSize;
AtLeastSize(int minSize) {
Preconditions.checkArgument(minSize >= 0);
this.minSize = minSize;
}
@Override
public boolean apply(Collection<?> c) {
return c.size() >= minSize;
}
}
/**
* Convenience method to prune key/values pairs where the size of the value collection is below a
* threshold.
*
* @param map The multimap to prune.
* @param minSize The minimum size for retained value collections.
* @param <K> The key type in the multimap.
* @param <V> The value type in the multimap.
* @return A new multimap, containing the pruned keys/values.
* @throws IllegalArgumentException if minSize < 0
*/
public static <K, V> Multimap<K, V> prune(Multimap<K, V> map, int minSize) {
return prune(map, new AtLeastSize(minSize));
}
/**
* Returns the set of keys associated with groups of a size greater than or equal to a given size.
*
* @param map The multimap to search.
* @param minSize The minimum size to return associated keys for.
* @param <K> The key type for the multimap.
* @return The keys associated with groups of size greater than or equal to {@code minSize}.
* @throws IllegalArgumentException if minSize < 0
*/
public static <K> Set<K> getLargeGroups(Multimap<K, ?> map, int minSize) {
return Sets.newHashSet(
Maps.filterValues(map.asMap(), new AtLeastSize(minSize)).keySet());
}
/**
* Returns the set of keys associated with the largest values in the multimap.
*
* @param map The multimap to search.
* @param topValues Number of groupings to find the keys for.
* @return The keys associated with the largest groups, of maximum size {@code topValues}.
*/
public static <K> Set<K> getLargestGroups(Multimap<K, ?> map, int topValues) {
Ordering<Multiset.Entry<K>> groupOrdering = new Ordering<Multiset.Entry<K>>() {
@Override
public int compare(Multiset.Entry<K> entry1, Multiset.Entry<K> entry2) {
return entry1.getCount() - entry2.getCount();
// overflow-safe, since sizes are nonnegative
}
};
Set<K> topKeys = Sets.newHashSetWithExpectedSize(topValues);
for (Multiset.Entry<K> entry
: groupOrdering.greatestOf(map.keys().entrySet(), topValues)) {
topKeys.add(entry.getElement());
}
return topKeys;
}
}