Core Guava Collections: Immutable, Mutable, and Special-Purpose Types Explained

Google Guava provides three families of core collections—immutable snapshots, enhanced mutable implementations, and special-purpose structures like Multimap, BiMap, and Table—that extend the JDK's standard library.

Google Guava's com.google.common.collect package enhances Java's standard collections framework with production-ready data structures designed for thread safety, performance, and expressiveness. These core Guava collections fall into three architectural families: immutable read-only types, mutable drop-in replacements with extra functionality, and specialized structures that have no direct JDK equivalent. Understanding these categories helps developers choose the right tool for everything from defensive copying to complex interval-based data modeling.

Immutable Collections: Thread-Safe Snapshots

Guava's immutable collections provide defensive copies that are inherently thread-safe and memory-efficient. Unlike Collections.unmodifiableList(), which wraps a mutable backing list, Guava's ImmutableList, ImmutableSet, and ImmutableMap are final classes that guarantee immutability at the API level.

ImmutableList, ImmutableSet, and ImmutableMap

The foundation of Guava's immutable family resides in ImmutableList.java, ImmutableSet.java, and ImmutableMap.java. These classes use structural sharing where possible and compact internal representations to minimize overhead. As implemented in google/guava, they disallow null elements and provide constant-time contains() operations for sets.

// Immutable collections – no modifications allowed after creation
ImmutableList<String> colors =
    ImmutableList.of("red", "green", "blue");

// Attempting to modify throws UnsupportedOperationException
// colors.add("yellow"); // Compilation error or runtime exception

ImmutableMultimap and ImmutableTable

For complex data structures, ImmutableMultimap.java and ImmutableTable.java provide immutable variants of Guava's special-purpose collections. These support the builder pattern for constructing large immutable instances efficiently.

// ImmutableTable – a 2‑dimensional map
ImmutableTable<String, String, Integer> grades =
    ImmutableTable.<String, String, Integer>builder()
        .put("alice", "math", 95)
        .put("bob", "history", 88)
        .build();

Mutable Core Collections: Enhanced JDK Replacements

Guava offers mutable collections that serve as drop-in replacements for standard JDK types while adding functionality like counting duplicates or maintaining bidirectional mappings. These implementations generally provide better scalability patterns than their standard library counterparts.

Multimap Implementations

The Multimap interface, defined in Multimap.java, represents a mapping from keys to multiple values (essentially Map<K, Collection<V>>). ArrayListMultimap.java provides a mutable implementation backed by ArrayList instances, while HashMultimap uses hash-based sets for values.

// Mutable Multimap – a key can map to many values
ArrayListMultimap<String, Integer> scores = ArrayListMultimap.create();
scores.put("alice", 10);
scores.put("alice", 8);
scores.put("bob", 7);

// alice now maps to [10, 8]
List<Integer> aliceScores = scores.get("alice");

Multiset and BiMap

HashMultiset.java implements the Multiset interface (also called a "bag"), which counts element occurrences. HashBiMap.java enforces a one-to-one relationship between keys and values, enabling inverse lookups via the inverse() method.

// BiMap – bidirectional map
BiMap<String, Integer> idByName = HashBiMap.create();
idByName.put("alice", 1);
idByName.put("bob", 2);
int aliceId = idByName.get("alice");          // → 1
String name = idByName.inverse().get(2);      // → "bob"

Special-Purpose Collections: Beyond Standard Maps

These data structures address specific use cases that standard Java collections handle poorly or not at all, including two-dimensional data and continuous interval modeling.

Table (Two-Dimensional Maps)

The Table interface, defined in Table.java, provides a matrix-like structure with row and column keys (Table<R, C, V>). HashBasedTable offers a mutable implementation using nested hash maps, while ImmutableTable provides the immutable variant.

RangeSet and RangeMap

RangeSet and RangeMap, backed by implementations like TreeRangeSet.java and TreeRangeMap.java, model continuous intervals using the Range<T> class. These red-black tree-backed structures maintain non-overlapping ranges and support logarithmic-time operations.

// RangeSet – store non‑overlapping integer intervals
RangeSet<Integer> rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closed(1, 5));
rangeSet.add(Range.open(10, 20));   // {1..5} ∪ (10,20)

// Query operations
boolean contains15 = rangeSet.contains(15); // false

Summary

  • Immutable collections (ImmutableList, ImmutableMap, etc.) provide thread-safe, read-only snapshots with internal structure sharing, implemented in files like ImmutableList.java and ImmutableMap.java.
  • Mutable core collections such as ArrayListMultimap and HashBiMap extend standard JDK interfaces with functionality for multiple values per key and bidirectional mappings.
  • Special-purpose types including Table, RangeSet, and RangeMap (defined in Table.java, RangeSet.java, and TreeRangeMap.java) handle two-dimensional data and continuous intervals without custom wrapper classes.
  • All Guava collections disallow null elements by default and integrate seamlessly with Java's standard collection interfaces.

Frequently Asked Questions

What makes Guava's immutable collections different from Collections.unmodifiableList?

Guava's immutable collections are truly immutable rather than merely unmodifiable views. While Collections.unmodifiableList() wraps a mutable list that can still be changed by other references, ImmutableList (as implemented in ImmutableList.java) uses final classes and defensive copying to guarantee that no modifications are possible after construction. This makes them inherently thread-safe without synchronization and safe to use as constants or return values from APIs.

When should I use ArrayListMultimap vs HashMultimap?

Use ArrayListMultimap (from ArrayListMultimap.java) when you need to preserve duplicate values for a single key or maintain insertion order, as it stores values in an ArrayList. Choose HashMultimap when you require set semantics for values—preventing duplicates per key—since it stores values in a HashSet. Both implement the Multimap interface but optimize for different access patterns and memory usage.

How does BiMap enforce unique values?

BiMap maintains two internal maps to ensure a one-to-one correspondence between keys and values. When calling put() on HashBiMap (implemented in HashBiMap.java), the method checks if the value already exists in the inverse map and removes the old key association if necessary. This constraint enables the inverse() method to return a view that swaps keys and values while maintaining the bidirectional mapping invariant.

Are Guava collections compatible with Java Streams?

Yes, all Guava core collections implement standard Java collection interfaces and work seamlessly with the Stream API. ImmutableList and ImmutableSet provide stream-like builder methods, while mutable collections like ArrayListMultimap support asMap() views that can be streamed. For specialized collections, RangeSet offers asRanges() and asDescendingSetOfRanges() methods that return Iterable views suitable for streaming operations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →