How to Use Guava BiMap for Inverse Map Lookup

A BiMap is useful in scenarios where you need to look up data in both directions.

For example, if you have a mapping between employee IDs and names, and you want to quickly retrieve an employee’s ID based on their name or vice versa, a BiMap can provide this functionality.

Guava provides an implementation of the BiMap interface called HashBiMap. Let’s see you can use it.

Adding Guava to our Project

If you’re using Maven, add the following dependency to the pom.xml file:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.0.1-jre</version> <!-- Check for the latest version -->
</dependency>

For Gradle, add this to the build.gradle:

implementation 'com.google.guava:guava:31.0.1-jre' // Check for the latest version

Create a BiMap and Get the Ieverse Map

    @Test
    public void testBiMap() {
      BiMap<Integer, String> empIDNameMap = HashBiMap.create();
      empIDNameMap.put(1, "Alice");
      empIDNameMap.put(2, "Tom");
      Assertions.assertEquals(2, empIDNameMap.inverse().get("Tom"));
    }

By calling inverse() method on biMap, we can get the employee id by employee name.

null as Inverse BiMap Key

BiMap also allows null value as key of inverse map.

    @Test
    public void testNullInverseKeyBiMap() {

        BiMap<Integer, String> empIDNameMap = HashBiMap.create();
        empIDNameMap.put(1, "Alice");
        empIDNameMap.put(2, null);
        Assertions.assertEquals(2, empIDNameMap.inverse().get(null));
    }

Add Entries to BiMap

Adding entries to empIdNameMap will also add a reverse entry to inverseMap, and entries added to inverseMap will also change the original empIdNameMap.

    @Test
    public void testAddEntryToBiMap() {

        BiMap<Integer, String> empIDNameMap = HashBiMap.create();
        empIDNameMap.put(1, "Alice");
        BiMap<String, Integer> inverseMap = empIDNameMap.inverse();
        empIDNameMap.put(4, "Joey");
        Assertions.assertEquals(4, inverseMap.get("Joey"));
    }

    @Test
    public void testAddEntryToInverseBiMap() {
        BiMap<Integer, String> empIDNameMap = HashBiMap.create();
        empIDNameMap.put(1, "Alice");
        BiMap<String, Integer> inverseMap = empIDNameMap.inverse();
        inverseMap.put("Tom", 3);
        Assertions.assertEquals("Tom", empIDNameMap.get(3));
    }

Remove Entries from BiMap

Removing entries from empIdNameMap will also remove the corresponding reverse entry from inverseMap, and entries removed from inverseMap will also change the original empIdNameMap.

  @Test
    public void testRemoveEntryFromBiMap() {

        BiMap<Integer, String> empIDNameMap = HashBiMap.create();
        empIDNameMap.put(1, "Alice");
        BiMap<String, Integer> inverseMap = empIDNameMap.inverse();
        empIDNameMap.remove(1);
        Assertions.assertNull(inverseMap.get("Alice"));
    }

    @Test
    public void testRemoveEntryFromInverseBiMap() {
        BiMap<Integer, String> empIDNameMap = HashBiMap.create();
        empIDNameMap.put(1, "Alice");
        BiMap<String, Integer> inverseMap = empIDNameMap.inverse();
        inverseMap.remove("Alice");
        Assertions.assertNull(empIDNameMap.get(1));
    }

Avoid Duplicate Values

BiMap ensures unique mappings in both directions, if you try to put a duplicate value, it will throw an IllegalArgumentException.


    @Test
    public void testDuplicateInverseKeys() {
        BiMap<Integer, String> empIDNameMap = HashBiMap.create();
        assertThrows(IllegalArgumentException.class,
                () -> {
                    empIDNameMap.put(1, "Alice");
                    empIDNameMap.put(2, "Alice");
                });
    }