Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Guava User Guide

Generated June 2026 from google/guava at 9a5d60d

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Guava is a set of core Java libraries from Google. It supplies the building blocks that the standard library leaves out: immutable collections, new collection types such as multimaps and multisets, a graph library, an in-memory cache, and polished utilities for concurrency, I/O, hashing, math, primitives, strings, and networking. This guide shows you how to use Guava from your own Java code — every section is grounded in the library's actual public API.

Note

Guava is a library, not an application. There is no command to run and nothing to deploy. You add it to your build and call its classes. Throughout this guide, "you" means a Java developer writing code against Guava's API.


1. Quick Start

Add the dependency, then start calling Guava.

Maven (pom.xml):

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>33.6.0-jre</version>
</dependency>

A first taste — immutable collections, null-safe optionals, and string joining:

import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;

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

String csv = Joiner.on(", ").join(colors);
System.out.println(csv);
red, green, blue
Optional<String> maybe = Optional.of("present");
System.out.println(maybe.isPresent());   // true
System.out.println(maybe.or("default")); // present
true
present

Tip

The fastest way to find a class is guava.dev. Append any class name to jump straight to its Javadoc — for example, guava.dev/ImmutableList. The full API index lives at guava.dev/api.


2. Installation

2.1 Coordinates and flavors

Guava's Maven group ID is com.google.guava and its artifact ID is guava. It ships in two flavors, selected by a suffix on the version string:

Flavor Version suffix Requires Use when
JRE -jre (e.g. 33.6.0-jre) JDK 1.8 or higher Server, desktop, or any standard JVM
Android -android (e.g. 33.6.0-android) Android, or any library that must stay Android-compatible Android apps, or libraries consumed by Android projects

Important

The flavor is chosen only through the version suffix — the group ID and artifact ID are identical for both. Mixing the two on one classpath is unsupported; pick one and be consistent across your whole dependency graph.

2.2 Adding Guava to your build

Maven

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>33.6.0-jre</version>
  <!-- or, for Android: -->
  <!-- <version>33.6.0-android</version> -->
</dependency>

Gradle (JRE)

dependencies {
  // Use Guava in your implementation only:
  implementation("com.google.guava:guava:33.6.0-jre")

  // Or, if Guava types appear in your own public API:
  api("com.google.guava:guava:33.6.0-jre")
}

Gradle (Android)

dependencies {
  // Implementation only:
  implementation("com.google.guava:guava:33.6.0-android")

  // Or, if Guava types appear in your public API:
  api("com.google.guava:guava:33.6.0-android")
}

Tip

Use implementation when Guava types stay inside your module, and api only when a Guava type appears in a method signature you expose to downstream modules. This keeps Guava off your consumers' compile classpath when they don't need it.

2.3 Always use the latest release

The version 33.6.0-jre shown above is the current release at the time of writing. Check the releases page for the newest version and update the suffix accordingly.

Warning

A version of the form 999.0.0-HEAD-jre-SNAPSHOT (or -android-SNAPSHOT) is a development snapshot built from the master branch, not a stable release. Use a numbered release like 33.6.0-jre in production builds unless you specifically need an unreleased fix.

2.4 What gets pulled in

Guava has exactly one dependency required for linkage at runtime: com.google.guava:failureaccess:1.0.3. Your build tool resolves it automatically. The remaining declared dependencies are annotation-only (used at compile time) and do not add runtime weight.

com.google.guava:guava:33.6.0-jre
└── com.google.guava:failureaccess:1.0.3   (the only runtime dependency)

3. Null Handling and Argument Checking

3.1 Optional — explicit absence

com.google.common.base.Optional<T> represents a value that may or may not be present, forcing callers to deal with absence instead of risking a NullPointerException.

import com.google.common.base.Optional;

Optional<Integer> present = Optional.of(5);   // throws if you pass null
Optional<Integer> absent  = Optional.absent();
Optional<Integer> nul     = Optional.fromNullable(getMaybeNull());

int a = present.get();          // 5  — throws IllegalStateException if absent
int b = absent.or(0);           // 0  — supply a default
boolean ok = present.isPresent(); // true

Useful operations on Optional:

Method Purpose
Optional.of(T) Wrap a non-null value (throws on null)
Optional.absent() An empty optional
Optional.fromNullable(T) Wrap a possibly-null value
get() The value, or IllegalStateException if absent
or(T default) The value, or a fallback
or(Supplier<T>) The value, or a lazily-computed fallback
transform(Function) Map the value if present
asSet() A 0- or 1-element immutable set
toJavaUtil() Convert to java.util.Optional

Note

If you target Java 8+, java.util.Optional covers most of the same ground. Guava's Optional predates it and remains useful for interop and for the transform/or(Supplier) style. toJavaUtil() and Optional.fromJavaUtil(...) bridge the two.

3.2 Preconditions — fail fast on bad input

com.google.common.base.Preconditions turns validation into one-liners that throw the right exception type with a clear message.

import static com.google.common.base.Preconditions.*;

void setAge(int age) {
  checkArgument(age >= 0, "age must be non-negative, was %s", age); // IllegalArgumentException
  this.age = age;
}

void start() {
  checkState(!running, "already running");                          // IllegalStateException
  this.config = checkNotNull(config, "config");                     // NullPointerException
}

String get(int index) {
  checkElementIndex(index, size);   // IndexOutOfBoundsException with a helpful message
  return data[index];
}
Method Throws Checks
checkArgument(boolean, ...) IllegalArgumentException A method argument
checkState(boolean, ...) IllegalStateException Object state
checkNotNull(T, ...) NullPointerException A reference is non-null (returns it)
checkElementIndex(index, size) IndexOutOfBoundsException A valid element index [0, size)
checkPositionIndex(index, size) IndexOutOfBoundsException A valid position [0, size]

Tip

The message templates use %s placeholders and are formatted lazily — the substitution only happens when the check actually fails, so there is no cost on the success path.

3.3 Verify — check invariants you expect to hold

com.google.common.base.Verify is Preconditions' sibling for verifying conditions that should already be true (it throws VerifyException rather than IllegalArgumentException/IllegalStateException).

import static com.google.common.base.Verify.*;

Bar bar = doSomething();
verify(bar.isOk(), "doSomething() returned a not-ok Bar: %s", bar);
String name = verifyNotNull(bar.name());

4. String Utilities

4.1 Splitter — predictable splitting

Unlike String.split, Splitter never silently drops trailing empty strings or applies surprising regex semantics. You configure it explicitly.

import com.google.common.base.Splitter;

// "a,b,,c" -> ["a", "b", "", "c"]   (empties kept by default)
List<String> raw = Splitter.on(',').splitToList("a,b,,c");

// Trim and drop empties:
List<String> clean = Splitter.on(',')
    .trimResults()
    .omitEmptyStrings()
    .splitToList(" a, b, ,c ");        // ["a", "b", "c"]

// Parse "key=val;key2=val2" into a Map:
Map<String, String> m = Splitter.on(';')
    .withKeyValueSeparator('=')
    .split("a=1;b=2");                 // {a=1, b=2}
Factory / modifier Effect
on(char) / on(String) / on(Pattern) Split on a separator
onPattern(String) Split on a regex given as a string
fixedLength(int) Split into fixed-size chunks
trimResults() / trimResults(CharMatcher) Trim each piece
omitEmptyStrings() Drop empty pieces
limit(int) Stop after N pieces
splitToList(...) / splitToStream(...) Materialize results
withKeyValueSeparator(...) Produce a Map

4.2 Joiner — the inverse of Splitter

import com.google.common.base.Joiner;

Joiner.on("; ").join(1, 2, 3);                         // "1; 2; 3"
Joiner.on(", ").skipNulls().join("a", null, "b");      // "a, b"
Joiner.on(", ").useForNull("N/A").join("a", null);     // "a, N/A"

Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 2);
Joiner.on(", ").withKeyValueSeparator("=").join(map);  // "a=1, b=2"

Caution

A Joiner is immutable; methods like skipNulls() and useForNull(...) return a new configured instance rather than mutating the original. Always use the returned value.

4.3 Strings, Ascii, and CaseFormat

import com.google.common.base.Strings;
import com.google.common.base.Ascii;
import com.google.common.base.CaseFormat;

Strings.padStart("7", 3, '0');           // "007"
Strings.padEnd("7", 3, '0');             // "700"
Strings.repeat("ab", 3);                 // "ababab"
Strings.commonPrefix("foobar", "foobaz");// "fooba"
Strings.nullToEmpty(null);               // ""
Strings.isNullOrEmpty("");               // true

Ascii.toUpperCase("hello");              // "HELLO" (ASCII-only, locale-independent)
Ascii.truncate("Hello, World", 8, "..."); // "Hello..."

// Convert identifier casing:
CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, "my_field"); // "MyField"
CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, "MyField");      // "my-field"

CaseFormat constants: LOWER_HYPHEN, LOWER_UNDERSCORE, LOWER_CAMEL, UPPER_CAMEL, UPPER_UNDERSCORE.

4.4 CharMatcher — match, count, trim, and replace characters

CharMatcher describes a class of characters and applies it across a CharSequence.

import com.google.common.base.CharMatcher;

CharMatcher digits = CharMatcher.inRange('0', '9');
digits.retainFrom("a1b2c3");                // "123"
digits.removeFrom("a1b2c3");                // "abc"
digits.countIn("a1b2c3");                   // 3

CharMatcher.whitespace().trimAndCollapseFrom("  a   b ", ' '); // "a b"
CharMatcher.anyOf("aeiou").replaceFrom("banana", '*');         // "b*n*n*"
Common factory Matches
any() / none() Everything / nothing
whitespace() / breakingWhitespace() Whitespace characters
ascii() ASCII characters
digit() / javaLetterOrDigit() Digits / letters & digits
is(char) / isNot(char) A single character
anyOf(CharSequence) / noneOf(...) Any/none of the given chars
inRange(char, char) A contiguous range

Matchers compose with negate(), and(...), and or(...).


5. Object Helpers: equals, hashCode, toString, and Comparison

5.1 MoreObjects and Objects

import com.google.common.base.MoreObjects;
import java.util.Objects;   // JDK

// Readable toString():
@Override public String toString() {
  return MoreObjects.toStringHelper(this)
      .add("id", id)
      .add("name", name)
      .omitNullValues()
      .toString();                // ClassName{id=42, name=Ada}
}

// First non-null:
String label = MoreObjects.firstNonNull(custom, "default");

Note

For equals and hashCode, Guava recommends the JDK's own java.util.Objects.equals(a, b) and java.util.Objects.hash(...) — there is no need for a Guava equivalent on Java 7+.

5.2 ComparisonChain — multi-field comparisons

import com.google.common.collect.ComparisonChain;

@Override public int compareTo(Person that) {
  return ComparisonChain.start()
      .compare(this.lastName, that.lastName)
      .compare(this.firstName, that.firstName)
      .compare(this.age, that.age)          // primitive int — no boxing
      .result();
}

The chain stops evaluating as soon as a non-zero result is found.

5.3 Ordering — power-tool comparators

Ordering<T> is a Comparator with extra combinators and helper methods.

import com.google.common.collect.Ordering;

Ordering<String> byLength = Ordering.natural()
    .nullsFirst()
    .onResultOf(String::length);

List<String> sorted = byLength.sortedCopy(list);
String longest = byLength.max(list);
List<String> top3 = Ordering.natural().greatestOf(numbers, 3); // 3 largest, descending
boolean ascending = Ordering.natural().isOrdered(list);

Tip

On Java 8+, the JDK's Comparator.comparing(...).thenComparing(...) covers many Ordering use cases. Reach for Ordering when you need leastOf/greatestOf (partial sort), isOrdered, or explicit(...) (a fixed custom order).


6. Immutable Collections

Immutable collections are Guava's most widely used feature. They cannot be modified after creation, which makes them inherently thread-safe, safe to share freely, and often more memory-efficient than a wrapped mutable collection.

import com.google.common.collect.*;

ImmutableList<String> list = ImmutableList.of("a", "b", "c");
ImmutableSet<Integer> set  = ImmutableSet.of(1, 2, 3);
ImmutableMap<String, Integer> map = ImmutableMap.of("one", 1, "two", 2);

// Builders for larger or computed contents:
ImmutableList<String> built = ImmutableList.<String>builder()
    .add("a")
    .addAll(moreItems)
    .build();

// Copy from an existing collection:
ImmutableSet<String> copy = ImmutableSet.copyOf(someCollection);

Available immutable types include ImmutableList, ImmutableSet, ImmutableSortedSet, ImmutableMap, ImmutableSortedMap, ImmutableBiMap, ImmutableMultiset, ImmutableMultimap (and its list/set variants), and ImmutableTable.

Warning

Every factory and builder rejects null elements, keys, and values with a NullPointerException. Immutable collections do not permit nulls — by design.

Important

ImmutableMap.of(...) and the default builder reject duplicate keys with an IllegalArgumentException. If later entries should win, use ImmutableMap.builder().put(...).buildKeepingLast(). To make a duplicate an explicit error, use buildOrThrow() (the same behavior as build()).

6.1 Building from a stream (Java 8+)

Guava provides Collectors so you can build immutable collections from a stream pipeline:

import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;

ImmutableList<String> upper = names.stream()
    .map(String::toUpperCase)
    .collect(toImmutableList());

ImmutableSet<Integer> evens = numbers.stream()
    .filter(n -> n % 2 == 0)
    .collect(toImmutableSet());

7. New Collection Types

Guava adds collection types the JDK lacks. Each is backed by familiar interfaces.

7.1 Multiset — a set that counts duplicates

import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;

Multiset<String> words = HashMultiset.create();
words.add("apple");
words.add("apple");
words.add("pear");

words.count("apple");       // 2
words.add("apple", 5);      // add 5 more in one call
words.elementSet();         // {apple, pear} — the distinct elements
words.size();               // total including duplicates

A multiset is ideal for histograms and frequency counts. Variants include HashMultiset, TreeMultiset, LinkedHashMultiset, and ImmutableMultiset.

7.2 Multimap — multiple values per key

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

Multimap<String, Integer> scores = ArrayListMultimap.create();
scores.put("ada", 90);
scores.put("ada", 95);
scores.put("bob", 70);

scores.get("ada");          // [90, 95] — a live List view
scores.keySet();            // {ada, bob}
scores.size();              // 3 (total key-value pairs)
Map<String, Collection<Integer>> asMap = scores.asMap();

Pick the implementation by the value-collection semantics you want:

Implementation Values per key Notes
ArrayListMultimap List (ordered, duplicates) General purpose
LinkedHashMultimap insertion-ordered, no duplicate pairs Predictable iteration
HashMultimap Set (no duplicate pairs)
TreeMultimap sorted keys and values
ImmutableListMultimap / ImmutableSetMultimap immutable Share freely

7.3 BiMap — a map with unique values

import com.google.common.collect.HashBiMap;
import com.google.common.collect.BiMap;

BiMap<String, Integer> ids = HashBiMap.create();
ids.put("ada", 1);
ids.inverse().get(1);       // "ada" — the inverse view

A BiMap enforces uniqueness of values as well as keys; putting a value that already exists throws IllegalArgumentException (use forcePut to override).

7.4 Table — a two-dimensional map

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;

Table<String, String, Integer> sales = HashBasedTable.create();
sales.put("Q1", "north", 100);
sales.put("Q1", "south", 80);

sales.get("Q1", "north");   // 100
sales.row("Q1");            // {north=100, south=80}
sales.column("north");      // {Q1=100}

7.5 RangeSet and RangeMap — intervals

import com.google.common.collect.*;

RangeSet<Integer> ranges = TreeRangeSet.create();
ranges.add(Range.closed(1, 10));
ranges.add(Range.closed(20, 30));
ranges.contains(5);                 // true
ranges.add(Range.closed(8, 22));    // merges into [1..30]

Range<Integer> r = Range.closedOpen(0, 100); // [0, 100)
r.contains(99);                     // true
r.contains(100);                    // false

Range factories: open, closed, closedOpen, openClosed, lessThan, atMost, greaterThan, atLeast, all, singleton. Ranges support intersection, span, gap, encloses, and isConnected.


8. Collection Utilities

Static helper classes mirror the collection types: Lists, Sets, Maps, Multimaps, Multisets, Iterables, Iterators, Streams, Collections2, Queues, Tables, and Comparators.

8.1 Factory methods with type inference

import com.google.common.collect.*;

List<String> names = Lists.newArrayList("ada", "bob");
Set<Integer> ids   = Sets.newHashSet(1, 2, 3);
Map<String, Integer> m = Maps.newHashMap();

ArrayList<String> sized = Lists.newArrayListWithExpectedSize(1000);

Note

On Java 7+ the diamond operator (new ArrayList<>()) makes these factories less essential, but the ...WithExpectedSize and ...WithCapacity variants still earn their keep when you know the target size up front.

8.2 Sets algebra

Set<Integer> a = ImmutableSet.of(1, 2, 3);
Set<Integer> b = ImmutableSet.of(2, 3, 4);

Sets.union(a, b);               // {1, 2, 3, 4}  (lazy view)
Sets.intersection(a, b);        // {2, 3}
Sets.difference(a, b);          // {1}
Sets.cartesianProduct(a, b);    // all ordered pairs
Sets.powerSet(a);               // all subsets

8.3 Maps transformations and diffs

Map<String, Integer> lengths = Maps.toMap(names, String::length);   // keys -> f(key)
Map<String, String> byId = Maps.uniqueIndex(people, Person::id);    // value -> indexed by key

MapDifference<String, Integer> diff = Maps.difference(left, right);
diff.entriesOnlyOnLeft();
diff.entriesDiffering();

8.4 Lists helpers

Lists.partition(bigList, 100);          // List of 100-element sublists
Lists.reverse(list);                    // reversed view
Lists.transform(list, String::trim);    // lazy transformed view
Lists.cartesianProduct(listA, listB);
Lists.charactersOf("hello");            // List<Character>

8.5 Iterables / Iterators

import com.google.common.collect.Iterables;

Iterables.getFirst(list, "default");
Iterables.getLast(list);
Iterables.getOnlyElement(list);                 // throws if size != 1
Iterables.concat(list1, list2, list3);          // lazy
Iterables.partition(iterable, 50);
Iterables.frequency(iterable, target);
boolean any = Iterables.any(list, s -> s.isEmpty());
Iterable<String> filtered = Iterables.filter(list, s -> s.length() > 3);

8.6 Streams and Multimaps/Multisets helpers

import com.google.common.collect.Streams;

Streams.stream(iterable);                       // Iterable -> Stream
Streams.zip(streamA, streamB, (a, b) -> a + b); // pairwise combine

// Group a list into a Multimap:
ImmutableListMultimap<Integer, String> byLen =
    Multimaps.index(words, String::length);

// Count occurrences into a Multiset, then read top counts:
Multiset<String> counts = HashMultiset.create(words);
Multisets.copyHighestCountFirst(counts);        // ordered by frequency

9. In-Memory Caching

com.google.common.cache.CacheBuilder builds a thread-safe, in-memory cache. A LoadingCache additionally computes (and remembers) values on demand via a CacheLoader.

import com.google.common.cache.*;
import java.util.concurrent.TimeUnit;

LoadingCache<String, User> users = CacheBuilder.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .recordStats()
    .build(new CacheLoader<String, User>() {
      @Override public User load(String id) {
        return database.lookupUser(id);   // called only on a miss
      }
    });

User u = users.getUnchecked("ada");       // loads on first call, cached after
CacheStats stats = users.stats();         // hitRate(), missCount(), ...
users.invalidate("ada");

Common CacheBuilder options:

Method Effect
maximumSize(long) Evict least-recently-used entries past this count
maximumWeight(long) + weigher(...) Bound by a custom weight instead of count
expireAfterWrite(Duration/long, unit) Expire entries a fixed time after creation
expireAfterAccess(Duration/long, unit) Expire entries a fixed time after last access
refreshAfterWrite(Duration/long, unit) Asynchronously refresh stale entries
weakKeys() / weakValues() / softValues() Allow GC to reclaim entries
removalListener(...) Be notified when entries are removed
recordStats() Enable hit/miss statistics

Note

Guava's own documentation marks CacheBuilder as discouraged in favor of Caffeine, a higher-performance cache with a similar API. Guava's cache remains fully supported and is a fine choice when you want to avoid an extra dependency, but consider Caffeine for new, performance-sensitive code.


10. Primitives

Per-primitive utility classes wrap the gaps in java.lang: Ints, Longs, Doubles, Floats, Shorts, Bytes, Chars, Booleans (plus SignedBytes, UnsignedBytes, UnsignedInts, UnsignedLongs). They share a consistent method set, illustrated here with Ints:

import com.google.common.primitives.Ints;

Ints.max(2, 7, 1);                       // 7
Ints.min(intArray);                      // smallest in the array
Ints.contains(intArray, 5);              // membership test
Ints.indexOf(intArray, 5);
Ints.concat(arr1, arr2);                 // join primitive arrays
Ints.toArray(listOfIntegers);            // List<Integer> -> int[]
Ints.asList(1, 2, 3);                    // int[] -> List<Integer> view
Ints.constrainToRange(value, 0, 100);    // clamp
Ints.checkedCast(longValue);             // long -> int, throws if it overflows
Ints.saturatedCast(longValue);           // long -> int, clamps instead of overflowing
Ints.join(",", 1, 2, 3);                 // "1,2,3"

Immutable primitive arrays — ImmutableIntArray, ImmutableLongArray, ImmutableDoubleArray — store primitives without boxing while remaining immutable.


11. Math

IntMath, LongMath, BigIntegerMath, and DoubleMath provide overflow-aware and rounding-aware arithmetic. Stats and Quantiles compute descriptive statistics.

import com.google.common.math.IntMath;
import com.google.common.math.Stats;
import com.google.common.math.Quantiles;
import java.math.RoundingMode;

IntMath.checkedAdd(Integer.MAX_VALUE, 1);     // throws ArithmeticException (overflow)
IntMath.saturatedAdd(Integer.MAX_VALUE, 1);   // Integer.MAX_VALUE (clamps)
IntMath.divide(7, 2, RoundingMode.CEILING);   // 4
IntMath.log2(1000, RoundingMode.UP);          // 10
IntMath.gcd(12, 18);                          // 6
IntMath.factorial(5);                         // 120
IntMath.isPrime(97);                          // true

Stats s = Stats.of(2, 4, 4, 4, 5, 5, 7, 9);
s.mean();                       // 5.0
s.populationStandardDeviation();
s.min(); s.max();

double median = Quantiles.median().compute(dataset);
double p95   = Quantiles.percentiles().index(95).compute(dataset);
Map<Integer, Double> quartiles = Quantiles.quartiles().indexes(1, 2, 3).compute(dataset);

Warning

checkedAdd, checkedMultiply, checkedPow, etc. throw ArithmeticException on overflow — use them when a silently-wrong result would be a bug. The saturated* variants clamp to MIN_VALUE/MAX_VALUE instead.


12. Hashing

com.google.common.hash offers strong, well-specified hash functions and the data structures that build on them, with a consistent fluent API.

import com.google.common.hash.*;
import java.nio.charset.StandardCharsets;

HashFunction sha256 = Hashing.sha256();
HashCode code = sha256.hashString("hello", StandardCharsets.UTF_8);
String hex = code.toString();           // lowercase hex

// Hash structured data with a Funnel:
HashCode h = Hashing.murmur3_128().newHasher()
    .putLong(id)
    .putString(name, StandardCharsets.UTF_8)
    .hash();

Available functions include md5(), sha1(), sha256(), sha384(), sha512(), murmur3_32(), murmur3_128(), sipHash24(), crc32(), crc32c(), adler32(), and the hmacSha* family.

Caution

md5() and sha1() are provided for compatibility with existing systems but are cryptographically broken. Do not use them for security purposes — prefer sha256() or stronger. (Guava marks md5() and sha1() as deprecated for exactly this reason.)

12.1 BloomFilter — probabilistic membership

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import java.nio.charset.StandardCharsets;

BloomFilter<String> seen = BloomFilter.create(
    Funnels.stringFunnel(StandardCharsets.UTF_8),
    1_000_000,      // expected insertions
    0.01);          // acceptable false-positive probability

seen.put("ada");
seen.mightContain("ada");   // true
seen.mightContain("zed");   // almost always false; never a false negative

Warning

BloomFilter.readFrom(InputStream, Funnel) reads a serialized filter whose size is encoded in the stream. Do not deserialize Bloom filters from untrusted input — a hostile stream can declare an enormous size and exhaust memory. Only read filters you produced yourself (this was reported as issue #8463).


13. I/O

The heart of com.google.common.io is the source/sink abstraction: a ByteSource/CharSource is a re-readable supplier of input, and a ByteSink/CharSink is a re-writable destination. They open and close streams for you.

import com.google.common.io.*;
import java.io.File;
import java.nio.charset.StandardCharsets;

// Read a whole file as text:
String text = Files.asCharSource(new File("notes.txt"), StandardCharsets.UTF_8).read();

// Read lines:
ImmutableList<String> lines =
    Files.asCharSource(new File("notes.txt"), StandardCharsets.UTF_8).readLines();

// Write text (truncating):
Files.asCharSink(new File("out.txt"), StandardCharsets.UTF_8).write("hello");

// Copy and compare:
Files.copy(new File("a.bin"), new File("b.bin"));
boolean same = Files.equal(new File("a.bin"), new File("b.bin"));

Files (and the NIO-based MoreFiles, which works with java.nio.file.Path) cover reading, writing, copying, moving, hashing, and traversal. Lower-level helpers:

Helper Purpose
ByteStreams.copy(in, out) / toByteArray(in) Stream-to-stream and stream-to-array
CharStreams.copy(...) / readLines(...) Character-stream equivalents
Resources.getResource(name) / toByteArray(url) Read classpath resources
BaseEncoding.base64() / base16() / base32() Encode/decode bytes ↔ text
Closer Safely close a group of Closeables, propagating the first failure
BaseEncoding.base64().encode(bytes);                 // standard Base64
byte[] back = BaseEncoding.base64().decode(encoded);
BaseEncoding.base16().lowerCase().encode(bytes);     // lowercase hex

// Hash a file directly through a ByteSource:
HashCode digest = Files.asByteSource(new File("a.bin")).hash(Hashing.sha256());

Note

Some I/O features — particularly around file metadata — are tested primarily on Linux and may behave differently on other platforms. See the warnings in the Admin Guide.


14. Concurrency

14.1 ListenableFuture and Futures

A ListenableFuture is a Future you can attach completion callbacks to, enabling composition without blocking.

import com.google.common.util.concurrent.*;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;

ListeningExecutorService pool =
    MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(4));

ListenableFuture<User> future = pool.submit(() -> loadUser(id));

// Transform without blocking:
ListenableFuture<String> name = Futures.transform(future, User::name, directExecutor());

// React to completion:
Futures.addCallback(future, new FutureCallback<User>() {
  @Override public void onSuccess(User u) { handle(u); }
  @Override public void onFailure(Throwable t) { log(t); }
}, directExecutor());

// Combine many:
ListenableFuture<List<User>> all = Futures.allAsList(f1, f2, f3);

Useful Futures methods: immediateFuture, immediateFailedFuture, transform, transformAsync, catching, withTimeout, allAsList, successfulAsList, whenAllComplete, whenAllSucceed, and submit.

Tip

Use MoreExecutors.directExecutor() for cheap, non-blocking callbacks that run on whatever thread completed the future. For anything that might block or take time, pass a real executor so you don't tie up the completing thread.

14.2 Service and ServiceManager

A Service models a component with a start/run/stop lifecycle. ServiceManager coordinates many services as a group.

import com.google.common.util.concurrent.*;

Service server = new AbstractIdleService() {
  @Override protected void startUp() { /* open ports */ }
  @Override protected void shutDown() { /* release resources */ }
};
server.startAsync().awaitRunning();
// ... later ...
server.stopAsync().awaitTerminated();

ServiceManager manager = new ServiceManager(ImmutableList.of(svcA, svcB, svcC));
manager.startAsync().awaitHealthy();

Base classes to extend: AbstractIdleService (no work between start and stop), AbstractExecutionThreadService (runs a loop on its own thread), and AbstractScheduledService (runs a task on a schedule).

14.3 Rate limiting, striped locks, and more

import com.google.common.util.concurrent.*;

RateLimiter limiter = RateLimiter.create(20.0);  // 20 permits/second
limiter.acquire();                                // block until a permit is available
if (limiter.tryAcquire()) { /* non-blocking */ }

Striped<Lock> locks = Striped.lock(64);           // fine-grained locking by key
Lock lock = locks.get(key);

ThreadFactory factory = new ThreadFactoryBuilder()
    .setNameFormat("worker-%d")
    .setDaemon(true)
    .build();

AtomicLongMap<String> counters = AtomicLongMap.create();
counters.incrementAndGet("requests");

Uninterruptibles provides interruption-safe versions of common blocking calls (sleepUninterruptibly, getUninterruptibly, awaitUninterruptibly, ...), and Monitor is a richer alternative to synchronized/Condition built around boolean Guards.


15. Networking

import com.google.common.net.*;

// Domain names with public-suffix awareness:
InternetDomainName name = InternetDomainName.from("www.google.co.uk");
name.topPrivateDomain();        // google.co.uk
name.publicSuffix();            // co.uk
name.isUnderPublicSuffix();     // true

// IP addresses:
InetAddress ip = InetAddresses.forString("2001:db8::1");
InetAddresses.toAddrString(ip); // canonical short form

// host:port parsing:
HostAndPort hp = HostAndPort.fromString("example.com:8080");
hp.getHost();                   // "example.com"
hp.getPortOrDefault(80);        // 8080

// Typed media types (constants provided):
MediaType json = MediaType.JSON_UTF_8;
MediaType png  = MediaType.PNG;

URL escaping lives in UrlEscapers:

UrlEscapers.urlPathSegmentEscaper().escape("a/b c");      // escape a path segment
UrlEscapers.urlFormParameterEscaper().escape("a=b&c");    // escape a query parameter

16. Escaping HTML, XML, and More

import com.google.common.html.HtmlEscapers;
import com.google.common.xml.XmlEscapers;

HtmlEscapers.htmlEscaper().escape("<b>Tom & Jerry</b>");
// &lt;b&gt;Tom &amp; Jerry&lt;/b&gt;

XmlEscapers.xmlContentEscaper().escape("a < b & c");
XmlEscapers.xmlAttributeEscaper().escape("\"quoted\"");

All escapers implement the common com.google.common.escape.Escaper interface, so they share the same escape(String) method.

Caution

These escapers protect against well-formedness problems, not against the full range of injection attacks. For HTML output in a security context, prefer a dedicated context-aware sanitizer.


17. Reflection

import com.google.common.reflect.*;

// Capture a full generic type (defeats erasure):
TypeToken<List<String>> token = new TypeToken<List<String>>() {};
token.getRawType();                 // List.class
token.resolveType(List.class.getTypeParameters()[0]); // String

// Scan the classpath:
ClassPath cp = ClassPath.from(getClass().getClassLoader());
for (ClassPath.ClassInfo info : cp.getTopLevelClasses("com.example.app")) {
  // info.getName(), info.load()
}

Invokable is a fluent wrapper over Method/Constructor, and AbstractInvocationHandler simplifies writing dynamic proxies.


18. EventBus (Publish/Subscribe)

EventBus lets components communicate without referencing each other directly.

import com.google.common.eventbus.*;

EventBus bus = new EventBus("app");

// A subscriber:
class OrderListener {
  @Subscribe public void onOrder(OrderPlaced event) {
    System.out.println("Order: " + event.id());
  }
}

bus.register(new OrderListener());
bus.post(new OrderPlaced("ORD-1"));     // delivered to matching @Subscribe methods

Use AsyncEventBus(Executor) to dispatch events on a background executor instead of the posting thread.

Note

Guava's own documentation marks EventBus as discouraged in favor of dependency injection and dedicated concurrency frameworks for new designs. It remains supported and is convenient for decoupling within a single process.


19. Graphs

com.google.common.graph models graph-structured data. There are three top-level interfaces:

Interface Edges carry Use for
Graph<N> nothing Plain node-and-edge graphs
ValueGraph<N, V> a value per edge Weighted/labeled edges
Network<N, E> first-class edge objects Parallel edges, edge identity
import com.google.common.graph.*;

MutableGraph<String> graph = GraphBuilder.directed()
    .allowsSelfLoops(false)
    .build();
graph.putEdge("a", "b");
graph.putEdge("a", "c");
graph.successors("a");          // [b, c]
graph.adjacentNodes("b");       // [a]

// Weighted edges:
MutableValueGraph<String, Integer> weighted = ValueGraphBuilder.undirected().build();
weighted.putEdgeValue("x", "y", 5);
weighted.edgeValueOrDefault("x", "y", 0);   // 5

// Traversal and analysis:
Iterable<String> bfs = Traverser.forGraph(graph).breadthFirst("a");
boolean cyclic = Graphs.hasCycle(graph);
Set<String> reachable = Graphs.reachableNodes(graph, "a");

Build immutable graphs with GraphBuilder.directed().immutable() (and the ValueGraph/Network equivalents). Traverser.forTree(...) is the cycle-free variant for tree-shaped data.


20. Best Practices

  • [x] Prefer immutable collections for fields, return values, and constants — they are thread-safe and document intent. Reach for a mutable type only when you actually mutate.
  • [x] Validate inputs at method entry with Preconditions. Fail fast with a clear message.
  • [x] Avoid @Beta APIs in libraries. Code annotated @Beta can change or disappear in any release. If your code is itself a library on others' classpaths, do not call @Beta APIs (or use the Guava Beta Checker to enforce this).
  • [x] Prefer JDK equivalents where they exist. On Java 8+, use java.util.Optional, Comparator.comparing, Objects.equals/hash, and the Streams API for the basics; use Guava for what the JDK still lacks (multimaps, immutable collections with non-null guarantees, ranges, caching, ListenableFuture, graphs, hashing).
  • [x] Use the right flavor. -android for Android or Android-compatible libraries; -jre everywhere else. Never mix both on one classpath.
  • [x] Don't deserialize Guava objects from untrusted input. Serialized forms are not stable across versions, and some types (notably BloomFilter) can be abused by hostile data.

Important

Guava's classes are not designed to defend against a malicious caller. Do not use them as a trust boundary between trusted and untrusted code.


21. Troubleshooting

Error: NullPointerException when building an immutable collection

Cause: You passed a null element, key, or value to an ImmutableList/ImmutableSet/ ImmutableMap factory or builder. Immutable collections reject nulls by design.

Solution: Filter nulls first, or use a type that permits them (e.g. a HashMap) if you truly need null values.

Prevention: Treat null as "absent" and model it with Optional instead of storing it.

Error: IllegalArgumentException: Multiple entries with same key

Cause: You supplied duplicate keys to ImmutableMap.of(...) or ImmutableMap.Builder.build().

Solution: Use ImmutableMap.builder()....buildKeepingLast() to let the last value win, or deduplicate before building. To make the duplicate an explicit failure, buildOrThrow() is the default build() behavior.

Prevention: When indexing a collection by a possibly-non-unique key, use a Multimap (via Multimaps.index) rather than a Map.

Error: Gradle unable to resolve Guava / VariantSelectionByAttributesException

Cause: A dependency-resolution conflict between the -jre and -android variants, often when one part of the dependency graph requests the Android flavor and another requests JRE (reported as issue #8398).

Solution: Pin a single flavor across your build (force com.google.guava:guava:33.6.0-jre or the -android equivalent), and ensure transitive dependencies agree.

Prevention: Declare Guava explicitly at the version and flavor you want so the resolver does not have to choose.

Problem: Suppliers.memoize() causes thread pinning under virtual threads

Cause: The memoizing supplier synchronizes while computing the value; under Project Loom virtual threads, holding a monitor across a blocking computation can pin the carrier thread (reported as issue #7140).

Solution: Keep the memoized computation short and non-blocking, or use a structure that does not hold a monitor across blocking work for hot paths on virtual threads.

Problem: Quantiles is very slow on data with many duplicate values

Cause: The quantile selection algorithm degrades on datasets dominated by duplicate values (reported as issue #3789).

Solution: Deduplicate or pre-aggregate before computing quantiles when your data has heavy repetition, or compute on a representative sample.

Problem: com.google.common.io behaves differently on Windows/macOS

Cause: Some I/O behaviors are validated primarily on Linux and may differ on other platforms.

Solution: Test platform-sensitive file operations on your target OS, and prefer the java.nio.file-based MoreFiles for portable file handling.


22. FAQ

Q: Which flavor should I use, -jre or -android? Use -jre for any standard JVM (server, desktop, build tools). Use -android for Android apps or for libraries that must remain Android-compatible. The two differ only in the version suffix.

Q: What is the minimum Java version? The JRE flavor requires JDK 1.8 or higher. The Android flavor targets Android (and is tested down to API level 23).

Q: Does Guava drag in lots of transitive dependencies? No. The only dependency needed at runtime is com.google.guava:failureaccess:1.0.3. The other declared dependencies are annotation-only and do not add runtime weight.

Q: What does the @Beta annotation mean? It marks an API that may change or be removed in any future release. Application code may use beta APIs, but library code should avoid them (or use the Guava Beta Checker to enforce that).

Q: Are deprecated APIs going to be removed? Generally no. Non-@Beta APIs remain binary-compatible indefinitely; even @Deprecated non-@Beta APIs are kept. The last release to remove non-@Beta APIs was Guava 21.0.

Q: Should I still use Guava's Optional now that java.util.Optional exists? Prefer java.util.Optional in new Java 8+ code. Keep Guava's Optional for interop with older APIs; toJavaUtil() and Optional.fromJavaUtil(...) convert between the two.

Q: Is Guava's cache the best choice for high-performance caching? Guava's documentation now points performance-sensitive users to Caffeine, which has a similar API. Guava's CacheBuilder remains supported and is a reasonable choice when you want to avoid an extra dependency.

Q: Can I persist Guava objects by serializing them? Don't rely on it. Serialized forms of Guava objects are subject to change across versions and are not guaranteed to be readable by a different version.

Q: Is Guava safe to use as a security boundary? No. Guava's classes are not designed to protect against a malicious caller; do not use them between trusted and untrusted code.

Q: Where is the API documentation? At guava.dev/api. You can also jump to any class by appending its name to guava.dev, e.g. guava.dev/ImmutableList.

Q: How do I build an immutable collection from a Java stream? Collect with ImmutableList.toImmutableList(), ImmutableSet.toImmutableSet(), or ImmutableMap.toImmutableMap(keyFn, valueFn).