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 atguava.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
implementationwhen Guava types stay inside your module, andapionly 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 themasterbranch, not a stable release. Use a numbered release like33.6.0-jrein 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.Optionalcovers most of the same ground. Guava'sOptionalpredates it and remains useful for interop and for thetransform/or(Supplier)style.toJavaUtil()andOptional.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
%splaceholders 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
Joineris immutable; methods likeskipNulls()anduseForNull(...)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
equalsandhashCode, Guava recommends the JDK's ownjava.util.Objects.equals(a, b)andjava.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 manyOrderinguse cases. Reach forOrderingwhen you needleastOf/greatestOf(partial sort),isOrdered, orexplicit(...)(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
nullelements, keys, and values with aNullPointerException. Immutable collections do not permit nulls — by design.
Important
ImmutableMap.of(...)and the default builder reject duplicate keys with anIllegalArgumentException. If later entries should win, useImmutableMap.builder().put(...).buildKeepingLast(). To make a duplicate an explicit error, usebuildOrThrow()(the same behavior asbuild()).
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...WithExpectedSizeand...WithCapacityvariants 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
CacheBuilderas 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. throwArithmeticExceptionon overflow — use them when a silently-wrong result would be a bug. Thesaturated*variants clamp toMIN_VALUE/MAX_VALUEinstead.
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()andsha1()are provided for compatibility with existing systems but are cryptographically broken. Do not use them for security purposes — prefersha256()or stronger. (Guava marksmd5()andsha1()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>");
// <b>Tom & Jerry</b>
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
EventBusas 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
@BetaAPIs in libraries. Code annotated@Betacan change or disappear in any release. If your code is itself a library on others' classpaths, do not call@BetaAPIs (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.
-androidfor Android or Android-compatible libraries;-jreeverywhere 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).