Guava Tips: Bimap

A rather common task is to model some relationship between two kinds of objects. Often you only need to go from a key to its value, and you will use a standard Map, but sometimes you will have to go back from the “value” to the “key”. In cases like this, the common solution is to build two maps:

Map<A, B> a2b = new HashMap<>();
Map<B, A> b2a = new HashMap<>();

a2b.put(myA, myB);
b2a.put(myB, myA);

However, this is quite cluttered and you can end up with subtle bug in case you forget to keep the two maps in sync, or fail to do so in case of concurrency.

Guava contains the very useful BiMap interface and some common implementation to solve the problem. A BiMap is a Map that can be inverted, meaning that is able to give you an inverse map that goes from the values to the keys. This means that you cannot insert duplicated values, as well as duplicated keys, since otherwise the map would not be invertible.

The precise definition, taken from the javadoc, is:

A bimap (or “bidirectional map”) is a map that preserves the uniqueness of its values as well as that of its keys. This constraint enables bimaps to support an “inverse view”, which is another bimap containing the same entries as this bimap but with reversed keys and values.

A BiMap’s inverse is a view: similarly to other Guava’s collections (such as Multimap), this means that the result of inverse is not a new map but rather an object that implements the correct interface, but uses the same underlying storage of the actual BiMap: any change done to the inverse view will reflect in the BiMap and viceversa.

Guava provides a few implementation of BiMap:

  • HashBiMap is the most commonly used one, and works just as a regular HashMap.
  • EnumHashBiMap is a special BiMap that can be useful when your keys are enums; just as EnumSet, it can be faster than a regular HashBiMap.
  • EnumBiMap is a special version of the above, useful when both your key and value are enums.
  • ImmutableBiMap is part of Guava’s immutable collections: it’s a BiMap that cannot be modified once it has been created. A Builder class is provided, similarly to the other immutable collections.

A quick example:

BiMap<A, B> bimap = HashBiMap.create();
bimap.put(myA, myB);
assertEquals(bimap.inverse().get(myB), myA);