4

Is there a more elegant/built-in way to reverse the keys and values of a Hashmap?

I currently have the following.

private Map<Boolean, List<String>> reverseMap(Map<String, Boolean> permissions) {
    List<String> allow = new ArrayList<String>();
    List<String> deny = new ArrayList<String>();
    Map<Boolean, List<String>> returnvalue = new HashMap<Boolean, List<String>>();

    for (Entry<String, Boolean> entry : permissions.entrySet()) {
        if(entry.getValue()) {
            allow.add(entry.getKey());
        } else {
            deny.add(entry.getKey());
        }
    }

    returnvalue.put(true, allow);
    returnvalue.put(false, deny);
    return returnvalue;
}
jpaugh
  • 6,634
  • 4
  • 38
  • 90
alexanderpas
  • 2,712
  • 2
  • 23
  • 33

4 Answers4

6

You might consider using one of Guava's Multimap implementations. For example:

private Multimap<Boolean, String> reverseMap(Map<String, Boolean> permissions) {
   Multimap<Boolean, String> multimap = ArrayListMultimap.create();
   for (Map.Entry<String, Boolean> entry : permissions.entrySet()) {
      multimap.put(entry.getValue(), entry.getKey());
   }
   return multimap;
}

Or more generally:

private static <K, V> Multimap<V, K> reverseMap(Map<K, V> source) {
   Multimap<V, K> multimap = ArrayListMultimap.create();
   for (Map.Entry<K, V> entry : source.entrySet()) {
      multimap.put(entry.getValue(), entry.getKey());
   }
   return multimap;
}
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
1

I'd do something similar (but if you must do this kind of thing frequently, consider Guava), only replacing the List with Set (seems a little more consistent) and prefilling the reversemap:

private Map<Boolean, Set<String>> reverseMap(Map<String, Boolean> permissions) {
    Map<Boolean, Set<String>> returnvalue = new HashMap<Boolean, Set<String>>();
    returnvalue.put(Boolean.TRUE, new HashSet<String>());
    returnvalue.put(Boolean.FALSE, new HashSet<String>());
    for (Entry<String, Boolean> entry : permissions.entrySet()) 
        returnvalue.get(entry.getValue()).add(entry.getKey());
    return returnvalue;
}
leonbloy
  • 73,180
  • 20
  • 142
  • 190
1

First thing to note is that you don't really need a reverse map if your values are only true or false. It will make sense if you have a broader range of values.

One easy (but not very elegant) way to get the entries with a specific value is:

public static <T, E> Set<T> getKeysByValue(Map<T, E> map, E value) {
     Set<T> keys = new HashSet<T>();
     for (Entry<T, E> entry : map.entrySet()) {
         if (entry.getValue().equals(value)) {
             keys.add(entry.getKey());
         }
     }
     return keys;
}

You can see that this is not so good if you need to call it every now and then. It makes sense to have two different maps (straight and reverse) and add entries to both. You can't use Bidi maps since there is no 1:1 relation between keys and values.

UPDATE: The following solution won't work. See comments. You can also consider using a TreeMap and keep it sorted based on the value. This way you can have a sorted set by calling map.entrySet() any time (denies entries first, then allows). The drawback is that it is only one set.

ValueComparator bvc =  new ValueComparator(map);
TreeMap<String,Boolean> sorted_map = new TreeMap(bvc);

class ValueComparator implements Comparator {
  Map base;

  public ValueComparator(Map base) {
      this.base = base;
  }

  public int compare(Object a, Object b) {
    return (Boolean)base.get(a).compareTo((Boolean)base.get(b));
  }
}

n0rm1e
  • 3,796
  • 2
  • 28
  • 37
  • I don't think the `TreeMap` sorted by the value in another map is a good idea - if the contents of the base map changes, then the tree in the `TreeMap` will be inconsistent, leading to unexpected results. – Timothy Jones Sep 26 '11 at 02:01
  • I meant using a TreeMap instead of the initial HashMap, sorry for the confusion ;-) – n0rm1e Sep 26 '11 at 02:29
  • If `base` is the TreeMap you're inserting into, then you'll get null pointer exceptions when inserting new things (since `base.get(newKey)` will be null). I don't think you can safely keep a TreeMap sorted by values :( – Timothy Jones Sep 26 '11 at 03:57
  • Writing a proper compare() method is left for the reader as a homework exercise ;-) By the way this compare method is called by TreeMap and won't be call on anything. – n0rm1e Sep 26 '11 at 05:12
  • The comparator will be used by the TreeMap for key lookup, which will prevent the Map from working correctly. I don't think you can use a TreeMap in this way - see http://stackoverflow.com/questions/2864840/treemap-sort-by-value/2864923#2864923 or http://stackoverflow.com/questions/7465369/automatically-sorted-by-values-map-in-java/7465402#7465402 – Timothy Jones Sep 26 '11 at 05:20
0

Guava's BiMap already provides a method for reversing its key-value pairs. Perhaps you could change the interface of the Map in question to BiMap, or else use the following code:

private BiMap<Boolean, String> reverseMap(Map<String, Boolean> permissions) {
   BiMap<String, Boolean> bimap = HashBiMap.create(permissions);
   return bimap.inverse();
}
Hunternif
  • 2,370
  • 2
  • 17
  • 14