Everything Java

← Back to blog

Published on Thu Jan 02 2025 12:00:00 GMT+0000 (Coordinated Universal Time) by Purusothaman Ramanujam

Google Guava Collections: Immutable Collections and Specialized Maps

Introduction

Google Guava provides powerful collection utilities that extend Java’s standard collections with immutable versions and specialized data structures. These utilities offer better performance, thread safety, and more intuitive APIs for common programming patterns.

In this post, we’ll explore Guava’s collection utilities including immutable collections, Multimap, and BiMap.

Adding Guava to Your Project

Maven Dependency

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

Gradle Dependency

implementation 'com.google.guava:guava:32.1.3-jre'

Immutable Collections

Guava provides immutable versions of collections that are thread-safe and more efficient:

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableMap;
public class ImmutableCollectionsExample {
public static void main(String[] args) {
// Immutable List
ImmutableList<String> list = ImmutableList.of("apple", "banana", "cherry");
System.out.println(list); // [apple, banana, cherry]
// Immutable Set
ImmutableSet<String> set = ImmutableSet.of("red", "green", "blue");
System.out.println(set); // [red, green, blue]
// Immutable Map
ImmutableMap<String, Integer> map = ImmutableMap.of(
"one", 1,
"two", 2,
"three", 3
);
System.out.println(map); // {one=1, two=2, three=3}
// Builder pattern for complex collections
ImmutableList<String> complexList = ImmutableList.<String>builder()
.add("first")
.addAll(list)
.add("last")
.build();
}
}

Benefits of Immutable Collections

  1. Thread Safety: Immutable collections are inherently thread-safe
  2. Performance: No synchronization overhead
  3. Memory Efficiency: Shared internal structures
  4. Defensive Programming: Prevents accidental modifications

Multimap

A Multimap allows multiple values for a single key, eliminating the need for Map<K, List<V>>:

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
public class MultimapExample {
public static void main(String[] args) {
Multimap<String, String> multimap = ArrayListMultimap.create();
multimap.put("fruits", "apple");
multimap.put("fruits", "banana");
multimap.put("fruits", "cherry");
multimap.put("colors", "red");
multimap.put("colors", "blue");
System.out.println(multimap.get("fruits")); // [apple, banana, cherry]
System.out.println(multimap.get("colors")); // [red, blue]
// Check if a key-value pair exists
System.out.println(multimap.containsEntry("fruits", "apple")); // true
// Remove specific key-value pair
multimap.remove("fruits", "banana");
// Remove all values for a key
multimap.removeAll("colors");
}
}

Multimap Implementations

BiMap

A BiMap is a bidirectional map where both keys and values must be unique:

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
public class BiMapExample {
public static void main(String[] args) {
BiMap<String, Integer> biMap = HashBiMap.create();
biMap.put("one", 1);
biMap.put("two", 2);
biMap.put("three", 3);
// Forward lookup
System.out.println(biMap.get("one")); // 1
// Reverse lookup
System.out.println(biMap.inverse().get(1)); // "one"
// This will throw an exception if the value already exists
// biMap.put("uno", 1); // IllegalArgumentException
// Force put (replaces existing entry)
biMap.forcePut("uno", 1);
}
}

BiMap Use Cases

  1. ID to Name mappings: User IDs to usernames
  2. Language translations: English to Spanish word mappings
  3. Configuration mappings: Property names to values
  4. Enum conversions: String representations to enum values

Table

Guava’s Table is a collection that maps row and column keys to values:

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
public class TableExample {
public static void main(String[] args) {
Table<String, String, Integer> table = HashBasedTable.create();
// Add data
table.put("John", "Math", 85);
table.put("John", "Science", 90);
table.put("Jane", "Math", 92);
table.put("Jane", "Science", 88);
// Get value
System.out.println(table.get("John", "Math")); // 85
// Get row
System.out.println(table.row("John")); // {Math=85, Science=90}
// Get column
System.out.println(table.column("Math")); // {John=85, Jane=92}
}
}

Best Practices

  1. Use Immutable Collections: Prefer immutable collections when possible for thread safety and performance
  2. Choose the Right Implementation: Select the appropriate Multimap implementation based on your needs
  3. Leverage BiMap for Bidirectional Lookups: Use BiMap when you need reverse lookups
  4. Use Table for Matrix-like Data: Table is perfect for spreadsheet-like data structures

Common Pitfalls

  1. Modifying Immutable Collections: Remember that immutable collections cannot be modified after creation
  2. BiMap Value Conflicts: Be careful with BiMap to avoid value conflicts
  3. Memory Usage: Large Multimaps can consume significant memory

Conclusion

Guava’s collection utilities provide powerful alternatives to standard Java collections. Immutable collections offer thread safety and performance benefits, while specialized collections like Multimap and BiMap solve common programming patterns more elegantly.

Start with immutable collections for better thread safety, then explore Multimap and BiMap when you need their specific functionality. These utilities can significantly improve your code’s readability and maintainability.

Resources

Written by Purusothaman Ramanujam

← Back to blog