On this page
Guava Basics
Google Guava provides extended collections, caching, string processing, and utility classes that fill gaps in the Java standard library.
Setup
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.0.0-jre</version>
</dependency>
Immutable Collections
Thread-safe, compact, fail-fast on modification:
ImmutableList<String> list = ImmutableList.of("a", "b", "c");
ImmutableSet<String> set = ImmutableSet.of("x", "y", "z");
ImmutableMap<String, Integer> map = ImmutableMap.of("one", 1, "two", 2);
// Builder for larger collections
ImmutableMap<String, User> users = ImmutableMap.<String, User>builder()
.put("alice", aliceUser)
.put("bob", bobUser)
.build();
Prefer over Collections.unmodifiable*() — truly immutable, not just unmodifiable view.
Multimap
One key → multiple values:
Multimap<String, String> multimap = ArrayListMultimap.create();
multimap.put("fruit", "apple");
multimap.put("fruit", "banana");
multimap.put("vegetable", "carrot");
Collection<String> fruits = multimap.get("fruit"); // [apple, banana]
Variants: ArrayListMultimap, HashMultimap, LinkedHashMultimap.
BiMap
Bidirectional map — value must be unique:
BiMap<String, Integer> biMap = HashBiMap.create();
biMap.put("one", 1);
biMap.put("two", 2);
biMap.inverse().get(1); // "one"
Table
Two-key map (row × column):
Table<String, String, Integer> sales = HashBasedTable.create();
sales.put("Q1", "Product A", 1000);
sales.put("Q1", "Product B", 1500);
sales.put("Q2", "Product A", 1200);
Integer q1ProductA = sales.get("Q1", "Product A"); // 1000
Map<String, Integer> q1Row = sales.row("Q1");
String Utilities
// Joiner
String result = Joiner.on(", ").skipNulls().join("a", null, "b"); // "a, b"
// Splitter
Iterable<String> parts = Splitter.on(',').trimResults().omitEmptyStrings()
.split("a, b, , c"); // ["a", "b", "c"]
// Strings
Strings.isNullOrEmpty(s); // null or ""
Strings.nullToEmpty(s); // null → ""
Strings.emptyToNull(s); // "" → null
// Case format conversion
CaseFormat.LOWER_HYPEN.to(CaseFormat.LOWER_CAMEL, "foo-bar"); // "fooBar"
Preconditions and Optional
// Argument validation
Preconditions.checkNotNull(user, "User must not be null");
Preconditions.checkArgument(age > 0, "Age must be positive, got %s", age);
Preconditions.checkState(isReady, "Service not ready");
// Optional utilities
Optional<User> opt = Optional.fromNullable(userRepository.findById(id));
User user = opt.or(userRepository.findByEmail(email));
Cache (Legacy — prefer Caffeine)
LoadingCache<String, User> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.recordStats()
.build(new CacheLoader<String, User>() {
@Override
public User load(String key) {
return userRepository.findByEmail(key).orElseThrow();
}
});
Rate Limiter
RateLimiter limiter = RateLimiter.create(10.0); // 10 permits per second
public void handleRequest() {
if (limiter.tryAcquire(1, Duration.ofMillis(100))) {
processRequest();
} else {
throw new RateLimitExceededException();
}
}
EventBus
Simple publish-subscribe within a JVM:
EventBus eventBus = new EventBus();
eventBus.register(new Object() {
@Subscribe
public void handleOrderCreated(OrderCreatedEvent event) {
sendConfirmationEmail(event);
}
});
eventBus.post(new OrderCreatedEvent(orderId));
For cross-JVM events, use Kafka or RabbitMQ instead.
Best Practices
- Use immutable collections for shared, read-only data
- Prefer Caffeine over Guava Cache for new projects
- Use
Preconditionsfor fast-fail argument validation - Guava RateLimiter is useful for in-process throttling
- Keep Guava updated — it’s widely used and well-maintained