How to swap arrayMap values and keys in Java

2019-02-19 22:41发布

问题:

I'm having a bit of trouble reversing a given map and storing its reversed keys and values into another map. I have a method prototype as follows:

public static Map<String, Set<String>> reverse (Map <String, Set<String>> graph);

So if I have sample keys for the directed graph such that:

{c -> arraySet{f, e}}
{b -> d}
{a -> arraySet{c, b}} 
{d -> g}
{e -> d}
{f -> arraySet{g, d}}

I need to effectively reverse this graph so that instead of b -> d I have d -> b.

I think all this requires is for me is to interchange the values and keys in the original graph and add them to the reverseMap. I suppose I could iterate through each set of values for a given key in the graph and then and store those in a list.

Unfortunately, I'm having trouble implementing this and thinking it through. I'd really appreciate a nudge in the right direction.

回答1:

You will need to loop over the entries in your map, and then, since the values are stored in a set, you will need to loop over that set. You will need to check your result map for each key and create a new set whenever a key does not yet exist.

public static Map<String, Set<String>> reverse (Map <String, Set<String>> graph) {
    Map<String, Set<String>> result = new HashMap<String, Set<String>>();
    for (Map.Entry<String, Set<String>> graphEntry: graph.entrySet()) {
        for (String graphValue: graphEntry.getValue()) {
            Set<String> set = result.get(graphValue);
            if (set == null) {
                set = new HashSet<String>();
                result.put(graphValue, set);
            }
            set.add(graphEntry.getKey());
        }
    }
    return result;
}


回答2:

Here's actual, working, up-to-date code using Guava Multimaps:

SetMultimap<Integer, Integer> graph = HashMultimap.create();
graph.put(1, 2); // add an edge from 1 to 2
SetMultimap<Integer, Integer> inverse = Multimaps.invertFrom(
  graph, HashMultimap.<Integer, Integer> create());

(Disclosure: I contribute to Guava.)

But if you can't use third-party libraries, do something like this...

Map<Integer, Set<Integer>> g;
Map<Integer, Set<Integer>> gInverse = new HashMap<Integer, Set<Integer>>();
for (Map.Entry<Integer, Set<Integer>> gAdj : g.entrySet()) {
  Integer v = gAdj.getKey();
  for (Integer w : gAdj.getValue()) {
    Set<Integer> wInverseAdj = gInverse.get(w);
    if (wInverseAdj == null) {
      gInverse.put(w, wInverseAdj = new HashSet<Integer>());
    }
    wInverseAdj.add(v);
  }
}

Or if you can use Java 8, use this...

map.entrySet().stream()
   .flatMap(entryKToVs -> entryKToVs.getValue().stream()
       .map(v -> new AbstractMap.SimpleEntry<>(entryKToVs.getKey(), str)))
   .collect(groupingBy(Map.Entry::getValue, mapping(Map.Entry::getKey, toList())))


回答3:

Pseudocode, but close to real. Just use a Multimap.

Multimap<String, String> ret = Multimaps.newSetMultimap();
for (Entry<String, Set<String>> entry : graph) {
  for(String neighbor : entry.getValue()) {
    ret.addTo(neighbor, entry.getKey());
  }
}


回答4:

I would first suggest writing a Graph class which abstracts away the underlying Map data structure. Of course, this just pushes the implementation to the Graph interface rather than dealing directly with the Map interface.

So for now, stick with your map:

Map<String, Set<String>> graph;
Map<String, Set<String>> reverse;

I suggest using Map.entrySet() to get the set of entries from your graph. Then for each Map.Entry, retrieve the key and value of each one. Next, for each "vertex" in the value Set, insert the key into the Set which is associated with the vertex.

This might be more clear if I formatted it similar to Java code rather than English sentences. I hope you get the basic idea. Of course, using a pre-made and pre-tested solution would be preferrable if it fits into your constraints of project.