How to Use Guava Immutable Collections: A Complete Guide to ImmutableList, ImmutableSet, and ImmutableMap

Use Guava immutable collections by creating immutable instances via static factory methods like ImmutableList.of() or ImmutableList.copyOf(), or use the Builder pattern for complex construction, ensuring thread-safe, null-safe, and unmodifiable data structures.

The google/guava library provides a robust framework for immutable collections that extend the standard Java Collections API with performance and safety guarantees. This guide covers the practical implementation of Guava immutable collections based on the actual source code in the com.google.common.collect package.

Why Use Guava Immutable Collections?

Guava immutable collections offer three primary advantages over standard mutable collections:

  • Thread-safety – No synchronization is required because the data cannot be mutated after creation.
  • Performance – Operations avoid defensive copying overhead; underlying data structures are stored efficiently.
  • Predictable behavior – Any attempt to modify the collection throws UnsupportedOperationException.

These characteristics make them ideal for constant data, configuration objects, and concurrent environments.

Core Architecture and Base Classes

All immutable collections in Guava extend the abstract class ImmutableCollection located at guava/src/com/google/common/collect/ImmutableCollection.java. This base class defines common immutable behavior and enforces the contract that elements cannot be added or removed after construction.

Concrete implementations include:

  • ImmutableList – Ordered sequence with indexed access
  • ImmutableSet – Unordered collection of unique elements
  • ImmutableMap – Key-value mappings with unique keys

Each concrete class resides in its respective source file (ImmutableList.java, ImmutableSet.java, ImmutableMap.java) and provides collection-specific factory methods.

Creating Guava Immutable Collections

Using Static Factory Methods (of() and copyOf())

The simplest way to create immutable collections is through static factory methods that perform null-checks on every element.

ImmutableList.of() creates instances from explicit elements (optimized for up to 12 arguments) or var-args:

ImmutableList<String> colors = ImmutableList.of("red", "green", "blue");

ImmutableList.copyOf(Iterable<? extends E>) creates an immutable copy from any existing Iterable while preserving order:

List<Integer> mutable = new ArrayList<>(Arrays.asList(1, 2, 3));
ImmutableList<Integer> immutable = ImmutableList.copyOf(mutable);

Similar factories exist for sets and maps:

ImmutableSet<Integer> primes = ImmutableSet.of(2, 3, 5, 7, 11);
ImmutableMap<String, Integer> ages = ImmutableMap.of("Alice", 30, "Bob", 25);

Using the Builder Pattern

For complex construction logic or conditional additions, use the builder() method which returns a mutable Builder instance. This pattern is defined in ImmutableList.java (line 69) and analogous classes:

ImmutableList.Builder<String> builder = ImmutableList.builder();
for (String s : someSource) {
    if (s.startsWith("A")) {
        builder.add(s);
    }
}
ImmutableList<String> aNames = builder.build();

The builder can be reused to create multiple immutable instances, making it efficient for batch processing.

Collecting from Java Streams

For Java 8+ stream processing, use toImmutableList() (defined in guava/src/com/google/common/collect/CollectCollectors.java) to collect results directly:

ImmutableList<Integer> squares = IntStream.rangeClosed(1, 5)
    .map(i -> i * i)
    .boxed()
    .collect(ImmutableList.toImmutableList());

Null Safety and Defensive Programming

All Guava immutable collection factories perform strict null-checks on each element. If any element is null, the factory immediately throws NullPointerException during construction rather than allowing nulls to exist in the supposedly immutable structure. This ensures that once created, the collection is truly immutable and free of null-related surprises.

Performance Characteristics

According to the source implementation in ImmutableCollection.java, these collections achieve performance through several optimizations:

  • No defensive copying – When using copyOf(), Guava analyzes the input to avoid unnecessary duplication if the source is already immutable.
  • Memory efficiency – Empty collections return singleton instances (e.g., ImmutableList.of() returns the same empty list reference every time).
  • Structural sharing – Builders use efficient array resizing strategies before finalizing the immutable structure.

Summary

  • Guava immutable collections reside in com.google.common.collect and extend ImmutableCollection.
  • Create instances via of() for literals, copyOf() for converting existing collections, or builder() for complex logic.
  • Use toImmutableList() (and similar collectors) for Stream API integration.
  • All factories reject null elements with NullPointerException to maintain immutability contracts.
  • Attempting modification operations throws UnsupportedOperationException.

Frequently Asked Questions

What happens if I try to modify a Guava immutable collection?

Any attempt to call mutating methods like add(), remove(), or clear() throws UnsupportedOperationException. This is enforced by the base implementation in ImmutableCollection.java and all concrete subclasses, ensuring runtime protection against accidental modification.

Can I create a Guava immutable collection containing null elements?

No. All factory methods and builders in ImmutableList, ImmutableSet, and ImmutableMap perform explicit null checks during construction. If you attempt to pass a null element, the method throws NullPointerException immediately, preventing nulls from entering the immutable data structure.

Are Guava immutable collections thread-safe?

Yes. Because the contents cannot change after creation, Guava immutable collections are inherently thread-safe without requiring synchronization. Multiple threads can safely read from the same instance simultaneously, as implemented in the thread-safe publication guarantees of the ImmutableCollection hierarchy.

When should I use copyOf() versus the Builder pattern?

Use copyOf() when you have an existing Iterable or collection that you want to convert to an immutable form in a single operation. Use the Builder pattern when you need to construct the collection programmatically with loops, conditionals, or multiple steps before finalizing the immutable instance.

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 →