19

How can we do that with Guava? Notice the presence of List<K> in the return type since many keys can map to the same value in any normal map.

public static <K, V> Map<V, List<K>> inverse(Map<K, V> map){
    Map<V, List<K>> result = new LinkedHashMap<V, List<K>>();
    for (Map.Entry<K, V> entry : map.entrySet()) {
        if(!result.containsKey(entry.getValue())){
            result.put(entry.getValue(), new ArrayList<K>());                
        }
        result.get(entry.getValue()).add(entry.getKey());
    }        
    return result;        
}

BiMap seems to insist on the unicity of the values, but I don't have this luxury.

ColinD
  • 108,630
  • 30
  • 201
  • 202
lacroix1547
  • 1,060
  • 1
  • 8
  • 16

3 Answers3

36

You can do this:

Map<K, V> map = ...;
ListMultimap<V, K> inverse = Multimaps.invertFrom(Multimaps.forMap(map), 
    ArrayListMultimap.<V,K>create());

Do note that pretty much any time you write Map<K, List<V>> or Map<K, Set<V>> or some such, a ListMultimap<K, V> or SetMultimap<K, V> is what you really want.

ColinD
  • 108,630
  • 30
  • 201
  • 202
  • 1
    But is is annoying to be forced to convert to Multimap. And considering the effort they put on performances, it may be improved in the future, with something more sexy. – lacroix1547 Sep 09 '10 at 17:16
  • 2
    @lacroix1547 Huh? `Multimaps.forMap()` returns a _view_ of the given map. It does almost no work... it just calls a constructor and assigns the map to a field. That's it. Consider it an adapter that allows you to use a map with methods like `invertFrom()` that expect a `Multimap`. – ColinD Sep 09 '10 at 17:24
  • Yeah ok I didnt opened forMap. I guess was right :) they put efforts on performances. – lacroix1547 Sep 09 '10 at 17:50
7

Use a Multimap instead, pick one that uses a list, like ArrayListMultimap, that will allow dupes.

Also you don't have to write your own invert method, there's one provided in com.google.common.collect.Multimaps.

ColinD
  • 108,630
  • 30
  • 201
  • 202
Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
1

In case someone stumbles here now (well within Java's Stream era), here are two single-expression Stream-based solutions:

1) Immutable version based on ImmutableListMultimap + toImmutableListMultimap collector

ImmutableListMultimap<V, K> output = inputMap.entrySet().stream()
        .collect(ImmutableListMultimap.toImmutableListMultimap(Map.Entry::getValue, Map.Entry::getKey));

2) Mutable version based on ArrayListMultimap + Multimaps.toMultimap collector

ListMultimap<V, K> output = inputMap.entrySet().stream()
        .collect(Multimaps.toMultimap(Map.Entry::getValue, Map.Entry::getKey, ArrayListMultimap::create));
Tomasz Linkowski
  • 4,386
  • 23
  • 38