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
- Thread Safety: Immutable collections are inherently thread-safe
- Performance: No synchronization overhead
- Memory Efficiency: Shared internal structures
- 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
- ArrayListMultimap: Values stored as ArrayList
- HashMultimap: Values stored as HashSet (no duplicates)
- TreeMultimap: Values stored as TreeSet (sorted)
- LinkedListMultimap: Values stored as LinkedList (preserves order)
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
- ID to Name mappings: User IDs to usernames
- Language translations: English to Spanish word mappings
- Configuration mappings: Property names to values
- 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
- Use Immutable Collections: Prefer immutable collections when possible for thread safety and performance
- Choose the Right Implementation: Select the appropriate Multimap implementation based on your needs
- Leverage BiMap for Bidirectional Lookups: Use BiMap when you need reverse lookups
- Use Table for Matrix-like Data: Table is perfect for spreadsheet-like data structures
Common Pitfalls
- Modifying Immutable Collections: Remember that immutable collections cannot be modified after creation
- BiMap Value Conflicts: Be careful with BiMap to avoid value conflicts
- 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