org.aksw.commons.collections.MultiMaps Maven / Gradle / Ivy
package org.aksw.commons.collections;
import com.google.common.collect.Sets;
import java.util.*;
/**
* Created by Claus Stadler
* Date: Oct 9, 2010
* Time: 5:42:34 PM
*
* A set of static methods for treating a Map> as a multimap
* (or rather graphs, as keys may be mapped to an empty set of values)
*
*/
public class MultiMaps
{
public static void putAll(Map> target, Map extends K, ? extends Set extends V>> source)
{
for(Map.Entry extends K, ? extends Set extends V>> entry : source.entrySet()) {
putAll(target, entry.getKey(), entry.getValue());
}
}
public static void putAll(Map> map, K key, Collection extends V> vs)
{
Set values = addKey(map, key);
values.addAll(vs);
}
public static void put(Map> map, K key, V value)
{
Set values = addKey(map, key);
values.add(value);
}
public static boolean containsEntry(Map> map, K key, V value)
{
Set values = map.get(key);
return values == null ? false : values.contains(value);
}
public static Collection values(Map> map)
{
return new FlatMapView(map.values());
}
public static Set addKey(Map> map, K key)
{
Set values = map.get(key);
if(values == null) {
values = new HashSet();
map.put(key, values);
}
return values;
}
public static void addAllKeys(Map> map, Iterable keys)
{
for(K key : keys) {
addKey(map, key);
}
}
public static Map> copy(Map> source)
{
Map> result = new HashMap>();
putAll(result, source);
return result;
}
/**
* Creates a new map that is the reverse of the source
*
* @param source
* @param
* @param
* @return
*/
public static Map> reverse(Map> source)
{
Map> result = new HashMap>();
for(Map.Entry> entry : source.entrySet()) {
for(V value : entry.getValue()) {
put(result, value, entry.getKey());
}
}
return result;
}
/**
* This method returns an empty set (Collections.emptySet) for keys that are not in the map.
*
* @param map
* @param key
* @param
* @param
* @return
*/
public static Set safeGet(Map> map, Object key)
{
Collection values = map.get(key);
return (values == null)
? Collections.emptySet()
: CollectionUtils.asSet(values);
}
/**
* Returns the set of successors for a given set of keys
*
* @param map
* @param keys
* @param
* @param
* @return
*/
public static Set safeGetAll(Map> map, Collection> keys) {
Set result = new HashSet();
for(Object key : keys) {
result.addAll(safeGet(map, key));
}
return result;
}
/**
* TODO Add to collection utils
*
* @param map
* @param key
* @param
* @param
* @return
*/
public static Collection safeGetC(Map> map, Object key)
{
Collection values = map.get(key);
return (values == null)
? Collections.emptySet()
: values;
}
public static Set transitiveGetAll(Map> map, Iterable> keys) {
Set result = new HashSet<>();
for(Object key : keys) {
Set tmp = transitiveGet(map, key);
result.addAll(tmp);
}
return result;
}
public static Set transitiveGet(Map> map, Object key)
{
Set result = new HashSet(safeGetC(map, key));
Set open = new HashSet(result);
Set next = new HashSet();
do {
for(T a : open) {
for(T b : safeGetC(map, a)) {
if(result.contains(b)) {
continue;
}
next.add(b);
}
}
open.clear();
result.addAll(next);
Set tmp = next;
next = open;
open = tmp;
} while(!open.isEmpty());
return result;
}
public static Map> transitiveClosure(Map> map)
{
return transitiveClosure(map, false);
}
public static Map> transitiveClosure(Map> map, boolean inPlace)
{
return transitiveClosureInPlace(inPlace ? map : copy(map));
}
public static Map> transitiveClosureInPlace(Map> map)
{
Map> open = map;
Map> next = new HashMap>();
for(;;) {
// Check if any edge following an open edge would create a new edge
for(Map.Entry> edge : open.entrySet()) {
T nodeA = edge.getKey();
for(T nodeB : edge.getValue()) {
for(T nodeC : safeGet(map, nodeB)) {
if(!containsEntry(map, nodeA, nodeC)) {
put(next, nodeA, nodeC);
}
}
}
}
// Exit condition
if(next.isEmpty()) {
return map;
}
// Preparation of next iteration
putAll(map, next);
if(open == map) {
open = new HashMap>();
} else {
open.clear();
}
Map> tmp = next;
next = open;
open = tmp;
}
}
/**
* Find the nearest set of nodes that are parents of both given nodes.
*
*
* @param map A mapping from children to parents
* @param a
* @param b
*/
public static Set getCommonParent(Map> map, T a, T b)
{
// The set of nodes for which all successors have already been visited
Set stableA = new HashSet();
Set stableB = new HashSet();
// The set of nodes for which their successors have not been visited yet
Set frontierA = new HashSet();
Set frontierB = new HashSet();
frontierA.add(a);
frontierB.add(b);
while(!frontierA.isEmpty() || !frontierB.isEmpty()) {
Set allA = Sets.union(frontierA, stableA);
Set allB = Sets.union(frontierB, stableB);
Set intersection = Sets.intersection(allA, allB);
// If there is an overlap in the nodes, we are done
if(!intersection.isEmpty()) {
return intersection;
}
// No overlap, keep looking - Move up one level
Set nextFrontierA = MultiMaps.safeGetAll(map, frontierA);
Set nextFrontierB = MultiMaps.safeGetAll(map, frontierB);
stableA.addAll(frontierA);
stableB.addAll(frontierB);
// OPTIMIZE Maybe this is more efficient: nextFrontierA.removeAll(Sets.intersection(frontierA,stableA));
// The intersection with the smaller set first would avoid scannig all nodes
// Note sure if java's removeAll already does such optimization
nextFrontierA.removeAll(stableA);
nextFrontierB.removeAll(stableB);
frontierA = nextFrontierA;
frontierB = nextFrontierB;
}
return Collections.emptySet();
}
/*
public static Map> transitiveClosureInPlace(Map> map)
{
return transitiveClosureInPlace(map, reverse(map));
}
public static Map> transitiveClosureInPlace(Map> map, Map> rev)
{
Map> open = map;
Map> next = new HashMap>();
for(;;) {
// Check if any edge leading to an open edge would create a new edge
for(Map.Entry> edgeB : open.entrySet()) {
for(T nodeA : safeGet(rev, edgeB.getKey())) {
for(T nodeC : edgeB.getValue()) {
if(!containsEntry(map, nodeA, nodeC)) {
// We would need the following statement if
// put(next, nodeA, nodeC) allowed duplicates
// put(map, nodeA, nodeC);
put(rev, nodeC, nodeA);
put(next, nodeA, nodeC);
}
}
}
}
// Exit condition
if(next.isEmpty()) {
break;
}
// Preparation of next iteration
putAll(map, next);
if(open == map) {
open = new HashMap>();
} else {
open.clear();
}
Map> tmp = next;
next = open;
open = tmp;
}// while (!open.isEmpty());
return map;
}
*/
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy