# Guava BiMap Use Cases and Implementation: A Complete Guide to Bidirectional Maps

> Explore Guava BiMap use cases and implementation. Learn how this bidirectional map enforces one-to-one mapping and offers constant-time reverse lookups via inverse().

- Repository: [Google/guava](https://github.com/google/guava)
- Tags: deep-dive
- Published: 2026-08-08

---

**Guava's `BiMap` is a specialized `Map` that enforces a strict one-to-one correspondence between keys and values, providing constant-time reverse lookups through the `inverse()` method while maintaining synchronized bidirectional views.**

Guava's `BiMap` (bidirectional map) eliminates the need to manually synchronize two separate hash maps when you require lookup operations in both directions. As implemented in the `google/guava` repository, this collection type guarantees that no two keys map to the same value, enabling safe inverse operations and offering specialized implementations optimized for mutable, immutable, and enum-based use cases.

## What is Guava BiMap?

A `BiMap` is a `Map` extension that maintains a bijection—a mathematical one-to-one relationship—between its key and value sets. Unlike standard `HashMap` implementations where values can collide, attempting to insert a key-value pair where the value already exists bound to a different key triggers an `IllegalArgumentException`. This constraint enables the `BiMap` to provide an `inverse()` method that returns a live view where keys become values and values become keys, backed by the same underlying data structure.

## Core Constraints and API Features

### Unique Value Enforcement

In [`com/google/common/collect/BiMap.java`](https://github.com/google/guava/blob/main/com/google/common/collect/BiMap.java), the interface contract specifies that the `put(K key, V value)` operation must verify value uniqueness before insertion. If the value is already present and mapped to a different key, the implementation throws `IllegalArgumentException` immediately. This check ensures the bidirectional integrity required for the `inverse()` view to remain valid.

### The forcePut Method for Overwriting

When you need to replace an existing mapping regardless of value collisions, `BiMap` provides `forcePut(K key, V value)`. According to the source implementation in [`HashBiMap.java`](https://github.com/google/guava/blob/main/HashBiMap.java), this method first removes any existing entry that uses the supplied value, then inserts the new key-value pair. This atomic replacement operation is essential for updating bidirectional mappings without manual cleanup.

### Bulk Operation Semantics

The `putAll(Map<? extends K, ? extends V> map)` operation in `BiMap` implementations respects the bijection constraint transactionally. As noted in the Guava source, if any entry in the bulk operation violates the unique value constraint, the operation aborts. However, earlier entries in the iteration sequence may have already been partially applied, leaving the map in an intermediate state.

### The Set<V> Values Collection

Unlike standard `Map` implementations where `values()` returns a `Collection<V>`, `BiMap` overrides this to return `Set<V>`. Because values must be unique by contract, they naturally form a set rather than a bag. This distinction affects return types in [`HashBiMap.java`](https://github.com/google/guava/blob/main/HashBiMap.java) and other implementations, allowing value-based set operations directly on the collection view.

## Real-World Use Cases for Bidirectional Maps

### Reverse Lookup Without Secondary Indexes

The primary use case for `BiMap` involves scenarios requiring O(1) reverse lookup without maintaining a secondary data structure. For example, mapping usernames to unique user IDs allows you to retrieve an ID from a username via `get()`, then later recover the username from the ID via `inverse().get()`. This eliminates synchronization bugs common when manually maintaining two separate `HashMap` instances.

### Encoding and Decoding Tables

URL shorteners and encoding schemes benefit from `BiMap`'s bijection guarantees. When mapping short codes to full URLs, the uniqueness constraint prevents accidental collisions where two codes point to the same URL. The `inverse()` view simplifies decoding: `codeToUrl.inverse().get(url)` immediately returns the corresponding short code without iteration.

### Enum-Based Configuration Mappings

For enum-to-value mappings where both the enum constants and target values are unique, `EnumBiMap` and `EnumHashBiMap` provide optimized alternatives to generic hash tables. As implemented in [`com/google/common/collect/EnumBiMap.java`](https://github.com/google/guava/blob/main/com/google/common/collect/EnumBiMap.java), these specialized classes use arrays for the forward mapping (enum ordinal indices), reducing memory overhead compared to hash buckets while maintaining the full `BiMap` contract.

## Implementation Architecture in Guava

### The BiMap Interface Contract

The `BiMap` interface defined in [`com/google/common/collect/BiMap.java`](https://github.com/google/guava/blob/main/com/google/common/collect/BiMap.java) extends `Map` and adds three critical operations: `forcePut()`, `inverse()`, and the `values()` override returning `Set<V>`. The interface establishes the mathematical bijection constraint that all implementations must enforce during mutation operations.

### HashBiMap: Dual HashMap Design

`HashBiMap`, located in [`com/google/common/collect/HashBiMap.java`](https://github.com/google/guava/blob/main/com/google/common/collect/HashBiMap.java), serves as the default mutable implementation. Internally, it maintains two `HashMap` instances: one for the forward direction (key → value) and one for the backward direction (value → key). All public mutation operations delegate to both maps atomically to preserve consistency. The `inverse()` method returns a wrapper instance that swaps the roles of these internal maps, creating a live view without data copying.

### ImmutableBiMap: Compact Array Storage

For immutable bidirectional mappings, [`com/google/common/collect/ImmutableBiMap.java`](https://github.com/google/guava/blob/main/com/google/common/collect/ImmutableBiMap.java) stores entries in compact arrays rather than hash tables. Because immutability prevents modifications, the implementation pre-computes the inverse mapping and stores it in a corresponding array structure. This design eliminates `IllegalArgumentException` risks during construction—the builder enforces uniqueness upfront—and provides faster iteration at the cost of slower construction.

### Enum Optimizations

`EnumBiMap` (in [`com/google/common/collect/EnumBiMap.java`](https://github.com/google/guava/blob/main/com/google/common/collect/EnumBiMap.java)) optimizes for enum keys by using an array indexed by enum ordinals for the forward mapping, while `EnumHashBiMap` uses a hash table for the value-to-key mapping. These implementations leverage the known finite domain of enum types to reduce memory allocation and improve lookup speed compared to generic hash-based approaches.

## Working with Guava BiMap: Code Examples

### Mutable Mappings with HashBiMap

The following example demonstrates creating a mutable bidirectional map, handling duplicate values, and using the inverse view:

```java
// Create a mutable HashBiMap
BiMap<String, Integer> userId = HashBiMap.create();
userId.put("alice", 1001);
userId.put("bob",   1002);

// Normal put throws if value already bound
// userId.put("charlie", 1001); // IllegalArgumentException

// Force‑put replaces the previous entry with the same value
userId.forcePut("charlie", 1001);   // "alice" is removed

// Reverse lookup via the inverse view
BiMap<Integer, String> idToUser = userId.inverse();
System.out.println(idToUser.get(1002)); // prints "bob"

// The inverse view stays in sync with the original map
userId.put("dave", 1003);
System.out.println(idToUser.get(1003)); // prints "dave"

// Removing an entry via either side updates both maps
idToUser.remove(1002); // removes "bob"
System.out.println(userId.containsKey("bob")); // false

```

### Immutable Bidirectional Maps

For configuration data that never changes, `ImmutableBiMap` provides type-safe bidirectional mappings with minimal memory overhead:

```java
ImmutableBiMap<String, Integer> colorCodes = ImmutableBiMap.of(
    "red",   1,
    "green", 2,
    "blue",  3);

// Inverse is also immutable
ImmutableBiMap<Integer, String> codeToColor = colorCodes.inverse();
System.out.println(codeToColor.get(2)); // prints "green"

// Attempting to modify either map throws UnsupportedOperationException
// colorCodes.put("yellow", 4); // RuntimeException

```

## Summary

- **Guava BiMap** enforces a one-to-one relationship between keys and values, throwing `IllegalArgumentException` when duplicate values are inserted via standard `put()`.

- The `inverse()` method returns a live view backed by the same underlying data, enabling constant-time reverse lookups without maintaining secondary data structures.

- **`forcePut()`** bypasses uniqueness checks by atomically removing conflicting entries before insertion, useful for updating existing mappings.

- **`HashBiMap`** implements the mutable contract using dual `HashMap` instances, while **`ImmutableBiMap`** uses compact arrays for memory-efficient immutable bidirectional maps.

- Specialized implementations **`EnumBiMap`** and **`EnumHashBiMap`** optimize for enum keys using array-based forward mappings.

## Frequently Asked Questions

### What happens when you try to put a duplicate value in a Guava BiMap?

Attempting to `put()` a key-value pair where the value already exists bound to a different key throws `IllegalArgumentException`. This behavior, enforced in all mutable implementations like `HashBiMap`, protects the bijection invariant required for the `inverse()` view to function correctly. To replace existing mappings regardless of value collisions, use the `forcePut()` method instead.

### How does BiMap inverse() work?

The `inverse()` method returns a live `BiMap` view where the original keys become values and original values become keys. In `HashBiMap`, this is implemented as a lightweight wrapper that swaps the internal forward and backward hash maps. Changes made to either the original map or its inverse are immediately visible in both directions because they share the same underlying data structures.

### What is the difference between HashBiMap and ImmutableBiMap?

`HashBiMap` is a mutable implementation using two `HashMap` instances to support read and write operations with O(1) complexity. `ImmutableBiMap` is an immutable, thread-safe implementation that stores entries in compact arrays and pre-computes its inverse during construction. The immutable variant cannot be modified after creation and enforces uniqueness constraints during the build phase rather than at insertion time.

### When should I use EnumBiMap instead of HashBiMap?

Use `EnumBiMap` when both keys and values are enum types, as it replaces hash tables with arrays indexed by enum ordinals, significantly reducing memory overhead and improving cache locality. `EnumHashBiMap` is appropriate when only the keys are enums and values are arbitrary objects, using an array for the forward mapping but a hash table for reverse lookups. Both provide better performance than generic `HashBiMap` for enum-constrained domains.