All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aksw.commons.collections.MultiMaps Maven / Gradle / Ivy

There is a newer version: 0.9.9
Show newest version
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> source)
    {
        for(Map.Entry> entry : source.entrySet()) {
            putAll(target, entry.getKey(), entry.getValue());
        }
    }

    public static  void putAll(Map> map, K key, Collection 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