Introduction to Guava Collections
Google Guava is a powerful open-source Java library that provides utility methods for collections, caching, primitives support, concurrency, common annotations, string processing, I/O, and more. The collections module is particularly rich, offering immutable collections, new collection types, and utility classes that greatly enhance Java's standard collections framework.
Maven Dependency
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>32.1.3-jre</version> </dependency>
Immutable Collections
Creating Immutable Collections
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import java.util.*;
public class ImmutableCollectionsExample {
public static void main(String[] args) {
// 1. Using of() method for small collections
ImmutableList<String> immutableList = ImmutableList.of("a", "b", "c");
ImmutableSet<Integer> immutableSet = ImmutableSet.of(1, 2, 3, 4, 5);
ImmutableMap<String, Integer> immutableMap = ImmutableMap.of(
"one", 1,
"two", 2,
"three", 3
);
System.out.println("Immutable List: " + immutableList);
System.out.println("Immutable Set: " + immutableSet);
System.out.println("Immutable Map: " + immutableMap);
// 2. Using copyOf() from existing collections
List<String> mutableList = new ArrayList<>();
mutableList.add("apple");
mutableList.add("banana");
mutableList.add("cherry");
ImmutableList<String> copiedList = ImmutableList.copyOf(mutableList);
System.out.println("Copied Immutable List: " + copiedList);
// 3. Using Builder for complex constructions
ImmutableList<String> builtList = ImmutableList.<String>builder()
.add("red")
.addAll(mutableList)
.add("blue", "green")
.build();
System.out.println("Built Immutable List: " + builtList);
// 4. Immutable Map with Builder
ImmutableMap<String, String> builtMap = ImmutableMap.<String, String>builder()
.put("key1", "value1")
.put("key2", "value2")
.put("key3", "value3")
.build();
System.out.println("Built Immutable Map: " + builtMap);
// 5. Attempting to modify immutable collections (will throw exception)
try {
immutableList.add("new element"); // Throws UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.out.println("Cannot modify immutable list: " + e.getMessage());
}
// 6. Immutable collections from arrays
String[] array = {"x", "y", "z"};
ImmutableList<String> fromArray = ImmutableList.copyOf(array);
System.out.println("From array: " + fromArray);
}
}
Advantages of Immutable Collections
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
public class ImmutableAdvantages {
// Thread-safe without synchronization
public static class SharedData {
private final ImmutableList<String> data;
public SharedData(List<String> input) {
this.data = ImmutableList.copyOf(input);
}
public List<String> getData() {
return data; // Safe to share without defensive copying
}
}
// Constants
public static final ImmutableList<String> DAYS_OF_WEEK = ImmutableList.of(
"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"
);
// Defensive copying in APIs
public static class DataProcessor {
private final ImmutableList<String> processedData;
public DataProcessor(List<String> rawData) {
// Defensive copy that's efficient and safe
this.processedData = ImmutableList.copyOf(rawData);
}
public List<String> getProcessedData() {
return processedData; // No need for defensive copy
}
}
public static void main(String[] args) {
// Memory efficiency - no synchronization needed
SharedData shared = new SharedData(List.of("a", "b", "c"));
// Multiple threads can safely access the data
Runnable task = () -> {
System.out.println(Thread.currentThread().getName() + ": " + shared.getData());
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
// Constants usage
System.out.println("Days: " + DAYS_OF_WEEK);
}
}
New Collection Types
Multiset (Bag)
import com.google.common.collect.*;
import java.util.*;
public class MultisetExample {
public static void main(String[] args) {
// 1. Creating and using Multiset
Multiset<String> multiset = HashMultiset.create();
// Add elements with counts
multiset.add("apple");
multiset.add("banana");
multiset.add("apple");
multiset.add("cherry");
multiset.add("banana");
multiset.add("apple");
System.out.println("Multiset: " + multiset);
System.out.println("Count of apple: " + multiset.count("apple"));
System.out.println("Count of banana: " + multiset.count("banana"));
System.out.println("Unique elements: " + multiset.elementSet());
// 2. Set specific counts
multiset.setCount("apple", 5);
System.out.println("After setting apple count to 5: " + multiset);
// 3. Remove elements
multiset.remove("apple", 2);
System.out.println("After removing 2 apples: " + multiset);
// 4. Different Multiset implementations
Multiset<String> treeMultiset = TreeMultiset.create();
treeMultiset.add("zebra");
treeMultiset.add("apple");
treeMultiset.add("banana");
treeMultiset.add("apple");
System.out.println("TreeMultiset (sorted): " + treeMultiset);
// 5. LinkedHashMultiset preserves insertion order
Multiset<String> linkedMultiset = LinkedHashMultiset.create();
linkedMultiset.add("zebra");
linkedMultiset.add("apple");
linkedMultiset.add("banana");
System.out.println("LinkedHashMultiset: " + linkedMultiset);
// 6. Practical example - word frequency counter
String text = "the quick brown fox jumps over the lazy dog the fox is quick";
Multiset<String> wordFrequency = HashMultiset.create();
for (String word : text.split(" ")) {
wordFrequency.add(word.toLowerCase());
}
System.out.println("\nWord frequencies:");
for (String word : wordFrequency.elementSet()) {
System.out.println(word + ": " + wordFrequency.count(word));
}
// 7. Converting to traditional collections
Set<String> uniqueWords = wordFrequency.elementSet();
System.out.println("Unique words: " + uniqueWords);
}
}
Multimap (One-to-Many Mapping)
import com.google.common.collect.*;
import java.util.*;
public class MultimapExample {
public static void main(String[] args) {
// 1. ArrayListMultimap - values stored in ArrayList
ArrayListMultimap<String, String> arrayListMultimap = ArrayListMultimap.create();
arrayListMultimap.put("fruits", "apple");
arrayListMultimap.put("fruits", "banana");
arrayListMultimap.put("fruits", "cherry");
arrayListMultimap.put("vegetables", "carrot");
arrayListMultimap.put("vegetables", "broccoli");
arrayListMultimap.put("fruits", "apple"); // Duplicates allowed
System.out.println("ArrayListMultimap: " + arrayListMultimap);
System.out.println("Fruits: " + arrayListMultimap.get("fruits"));
System.out.println("Vegetables: " + arrayListMultimap.get("vegetables"));
// 2. HashMultimap - values stored in HashSet (no duplicates)
HashMultimap<String, String> hashMultimap = HashMultimap.create();
hashMultimap.put("colors", "red");
hashMultimap.put("colors", "blue");
hashMultimap.put("colors", "green");
hashMultimap.put("colors", "red"); // Duplicate ignored
System.out.println("\nHashMultimap: " + hashMultimap);
System.out.println("Colors: " + hashMultimap.get("colors"));
// 3. LinkedHashMultimap - preserves insertion order
LinkedHashMultimap<String, String> linkedMultimap = LinkedHashMultimap.create();
linkedMultimap.put("letters", "z");
linkedMultimap.put("letters", "a");
linkedMultimap.put("letters", "c");
System.out.println("\nLinkedHashMultimap: " + linkedMultimap);
System.out.println("Letters: " + linkedMultimap.get("letters"));
// 4. TreeMultimap - sorted keys and values
TreeMultimap<String, Integer> treeMultimap = TreeMultimap.create();
treeMultimap.put("scores", 85);
treeMultimap.put("scores", 92);
treeMultimap.put("scores", 78);
treeMultimap.put("ages", 25);
treeMultimap.put("ages", 30);
System.out.println("\nTreeMultimap: " + treeMultimap);
// 5. Practical example - student courses
Multimap<String, String> studentCourses = ArrayListMultimap.create();
studentCourses.put("Alice", "Math");
studentCourses.put("Alice", "Physics");
studentCourses.put("Bob", "Chemistry");
studentCourses.put("Bob", "Biology");
studentCourses.put("Bob", "Math");
studentCourses.put("Charlie", "Physics");
System.out.println("\nStudent Courses:");
for (String student : studentCourses.keySet()) {
System.out.println(student + ": " + studentCourses.get(student));
}
// 6. Views and transformations
Map<String, Collection<String>> asMap = studentCourses.asMap();
System.out.println("\nAs Map: " + asMap);
// 7. Remove and replace operations
studentCourses.remove("Alice", "Physics");
System.out.println("After removing Physics from Alice: " + studentCourses.get("Alice"));
studentCourses.replaceValues("Bob", Arrays.asList("Art", "Music"));
System.out.println("After replacing Bob's courses: " + studentCourses.get("Bob"));
}
}
BiMap (Two-Way Map)
import com.google.common.collect.*;
import java.util.*;
public class BiMapExample {
public static void main(String[] args) {
// 1. HashBiMap - two-way mapping
HashBiMap<String, Integer> biMap = HashBiMap.create();
biMap.put("one", 1);
biMap.put("two", 2);
biMap.put("three", 3);
System.out.println("BiMap: " + biMap);
System.out.println("Key for value 2: " + biMap.inverse().get(2));
System.out.println("Value for key 'three': " + biMap.get("three"));
// 2. Inverse view
BiMap<Integer, String> inverseBiMap = biMap.inverse();
System.out.println("Inverse BiMap: " + inverseBiMap);
// 3. Force put - will throw if value already exists
try {
biMap.put("uno", 1); // Throws IllegalArgumentException
} catch (IllegalArgumentException e) {
System.out.println("Cannot put duplicate value: " + e.getMessage());
}
// 4. Force put with overwrite
biMap.forcePut("uno", 1); // Removes "one" -> 1 mapping
System.out.println("After forcePut: " + biMap);
// 5. EnumBiMap - for enum keys and values
enum Color { RED, GREEN, BLUE }
enum Code { R, G, B }
EnumBiMap<Color, Code> enumBiMap = EnumBiMap.create(Color.class, Code.class);
enumBiMap.put(Color.RED, Code.R);
enumBiMap.put(Color.GREEN, Code.G);
enumBiMap.put(Color.BLUE, Code.B);
System.out.println("EnumBiMap: " + enumBiMap);
System.out.println("Inverse: " + enumBiMap.inverse());
// 6. Practical example - country codes
HashBiMap<String, String> countryCodes = HashBiMap.create();
countryCodes.put("United States", "US");
countryCodes.put("United Kingdom", "UK");
countryCodes.put("Canada", "CA");
countryCodes.put("Australia", "AU");
System.out.println("\nCountry Codes:");
System.out.println("Code for Canada: " + countryCodes.get("Canada"));
System.out.println("Country for AU: " + countryCodes.inverse().get("AU"));
// 7. ImmutableBiMap
ImmutableBiMap<String, Integer> immutableBiMap = ImmutableBiMap.of(
"first", 1,
"second", 2,
"third", 3
);
System.out.println("\nImmutableBiMap: " + immutableBiMap);
System.out.println("Inverse: " + immutableBiMap.inverse());
}
}
Table (Two-Dimensional Map)
import com.google.common.collect.*;
import java.util.*;
public class TableExample {
public static void main(String[] args) {
// 1. HashBasedTable - most commonly used
Table<String, String, Double> distanceTable = HashBasedTable.create();
distanceTable.put("New York", "Boston", 215.0);
distanceTable.put("New York", "Washington DC", 225.0);
distanceTable.put("Boston", "Washington DC", 440.0);
distanceTable.put("Chicago", "Detroit", 283.0);
System.out.println("Distance Table: " + distanceTable);
System.out.println("NY to Boston: " + distanceTable.get("New York", "Boston"));
// 2. Row and column views
Map<String, Double> nyDistances = distanceTable.row("New York");
System.out.println("Distances from NY: " + nyDistances);
Map<String, Double> bostonDistances = distanceTable.column("Boston");
System.out.println("Distances to Boston: " + bostonDistances);
// 3. TreeBasedTable - sorted rows and columns
TreeBasedTable<String, String, Integer> sortedTable = TreeBasedTable.create();
sortedTable.put("zebra", "apple", 1);
sortedTable.put("apple", "zebra", 2);
sortedTable.put("banana", "cherry", 3);
System.out.println("\nTreeBasedTable (sorted): " + sortedTable);
// 4. ArrayTable - fixed rows and columns
List<String> rows = Arrays.asList("row1", "row2", "row3");
List<String> columns = Arrays.asList("col1", "col2", "col3");
ArrayTable<String, String, String> arrayTable = ArrayTable.create(rows, columns);
arrayTable.put("row1", "col1", "value11");
arrayTable.put("row1", "col2", "value12");
arrayTable.put("row2", "col1", "value21");
System.out.println("\nArrayTable: " + arrayTable);
// 5. Practical example - student grades
Table<String, String, Integer> gradeTable = HashBasedTable.create();
gradeTable.put("Alice", "Math", 85);
gradeTable.put("Alice", "Science", 92);
gradeTable.put("Bob", "Math", 78);
gradeTable.put("Bob", "Science", 88);
gradeTable.put("Charlie", "Math", 95);
gradeTable.put("Charlie", "Science", 90);
System.out.println("\nGrade Table:");
System.out.println("Alice's Math grade: " + gradeTable.get("Alice", "Math"));
// 6. Row and column operations
Set<String> students = gradeTable.rowKeySet();
Set<String> subjects = gradeTable.columnKeySet();
System.out.println("Students: " + students);
System.out.println("Subjects: " + subjects);
// 7. Cell iteration
System.out.println("\nAll grades:");
for (Table.Cell<String, String, Integer> cell : gradeTable.cellSet()) {
System.out.printf("%s - %s: %d%n",
cell.getRowKey(), cell.getColumnKey(), cell.getValue());
}
// 8. Transforming tables
Table<String, String, String> formattedTable = Tables.transformValues(
gradeTable, grade -> grade >= 90 ? "A" : grade >= 80 ? "B" : "C"
);
System.out.println("\nFormatted Grades: " + formattedTable);
}
}
Collection Utilities
Lists Utilities
import com.google.common.collect.*;
import java.util.*;
public class ListsUtilitiesExample {
public static void main(String[] args) {
// 1. Creating lists with factory methods
List<String> list1 = Lists.newArrayList("a", "b", "c");
List<Integer> list2 = Lists.newArrayList(1, 2, 3, 4, 5);
List<String> list3 = Lists.newArrayListWithCapacity(100);
System.out.println("List1: " + list1);
System.out.println("List2: " + list2);
// 2. Transformations and partitions
List<List<Integer>> partitions = Lists.partition(list2, 2);
System.out.println("Partitions of size 2: " + partitions);
// 3. Reverse view
List<String> reversed = Lists.reverse(list1);
System.out.println("Reversed: " + reversed);
// 4. Character lists from strings
List<Character> chars = Lists.charactersOf("hello");
System.out.println("Characters: " + chars);
// 5. Cartesian product
List<List<String>> cartesian = Lists.cartesianProduct(
Arrays.asList("a", "b"),
Arrays.asList("1", "2")
);
System.out.println("Cartesian product: " + cartesian);
// 6. Transform with function
List<String> transformed = Lists.transform(list2, i -> "number-" + i);
System.out.println("Transformed: " + transformed);
// Practical examples
List<String> names = Lists.newArrayList("Alice", "Bob", "Charlie", "David");
// Batch processing
int batchSize = 2;
List<List<String>> batches = Lists.partition(names, batchSize);
System.out.println("\nBatch processing:");
for (int i = 0; i < batches.size(); i++) {
System.out.println("Batch " + (i + 1) + ": " + batches.get(i));
}
// Reverse for LIFO behavior
List<String> stackLike = Lists.newArrayList();
stackLike.add("first");
stackLike.add("second");
stackLike.add("third");
List<String> lifoView = Lists.reverse(stackLike);
System.out.println("\nLIFO view: " + lifoView);
System.out.println("First to process: " + lifoView.get(0));
}
}
Sets Utilities
import com.google.common.collect.*;
import java.util.*;
public class SetsUtilitiesExample {
public static void main(String[] args) {
Set<String> set1 = Sets.newHashSet("a", "b", "c", "d");
Set<String> set2 = Sets.newHashSet("c", "d", "e", "f");
System.out.println("Set1: " + set1);
System.out.println("Set2: " + set2);
// 1. Union
Set<String> union = Sets.union(set1, set2);
System.out.println("Union: " + union);
// 2. Intersection
Set<String> intersection = Sets.intersection(set1, set2);
System.out.println("Intersection: " + intersection);
// 3. Difference
Set<String> difference = Sets.difference(set1, set2);
System.out.println("Difference (set1 - set2): " + difference);
// 4. Symmetric difference
Set<String> symmetricDifference = Sets.symmetricDifference(set1, set2);
System.out.println("Symmetric difference: " + symmetricDifference);
// 5. Power set
Set<String> smallSet = Sets.newHashSet("a", "b", "c");
Set<Set<String>> powerSet = Sets.powerSet(smallSet);
System.out.println("\nPower set of " + smallSet + ":");
for (Set<String> subset : powerSet) {
System.out.println(" " + subset);
}
// 6. Cartesian product
Set<List<String>> cartesian = Sets.cartesianProduct(
Sets.newHashSet("x", "y"),
Sets.newHashSet("1", "2")
);
System.out.println("\nCartesian product: " + cartesian);
// 7. Filtering sets
Set<String> filtered = Sets.filter(set1, s -> s.compareTo("c") >= 0);
System.out.println("Filtered (>= 'c'): " + filtered);
// 8. Combinations
Set<Set<String>> combinations = Sets.combinations(smallSet, 2);
System.out.println("\nCombinations of size 2: " + combinations);
// Practical example - tag system
Set<String> user1Tags = Sets.newHashSet("java", "programming", "coding");
Set<String> user2Tags = Sets.newHashSet("programming", "development", "coding");
Set<String> commonTags = Sets.intersection(user1Tags, user2Tags);
Set<String> allTags = Sets.union(user1Tags, user2Tags);
Set<String> uniqueToUser1 = Sets.difference(user1Tags, user2Tags);
System.out.println("\nTag Analysis:");
System.out.println("Common tags: " + commonTags);
System.out.println("All tags: " + allTags);
System.out.println("Unique to user1: " + uniqueToUser1);
}
}
Maps Utilities
import com.google.common.collect.*;
import java.util.*;
import java.util.function.Function;
public class MapsUtilitiesExample {
public static void main(String[] args) {
// 1. Creating maps
Map<String, Integer> map1 = Maps.newHashMap();
Map<String, String> map2 = Maps.newLinkedHashMap();
// 2. Unique index
List<Person> people = Arrays.asList(
new Person(1, "Alice"),
new Person(2, "Bob"),
new Person(3, "Charlie")
);
Map<Integer, Person> idToPerson = Maps.uniqueIndex(people, Person::getId);
System.out.println("ID to Person map: " + idToPerson);
// 3. Transform values
Map<Integer, String> idToName = Maps.transformValues(idToPerson, Person::getName);
System.out.println("ID to Name map: " + idToName);
// 4. Difference between maps
Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
Map<String, Integer> right = ImmutableMap.of("a", 1, "b", 4, "d", 5);
MapDifference<String, Integer> difference = Maps.difference(left, right);
System.out.println("\nMap Difference:");
System.out.println("Entries in common: " + difference.entriesInCommon());
System.out.println("Entries differing: " + difference.entriesDiffering());
System.out.println("Entries only in left: " + difference.entriesOnlyOnLeft());
System.out.println("Entries only in right: " + difference.entriesOnlyOnRight());
// 5. Filtering maps
Map<String, Integer> filtered = Maps.filterValues(left, value -> value > 1);
System.out.println("Filtered (values > 1): " + filtered);
// 6. AsMap from set
Set<String> keys = Sets.newHashSet("key1", "key2", "key3");
Map<String, String> generatedMap = Maps.asMap(keys, key -> "value-for-" + key);
System.out.println("Generated map: " + generatedMap);
// Practical example - configuration processing
Map<String, String> defaultConfig = ImmutableMap.of(
"host", "localhost",
"port", "8080",
"timeout", "30"
);
Map<String, String> userConfig = ImmutableMap.of(
"host", "example.com",
"port", "9090",
"database", "mydb"
);
MapDifference<String, String> configDiff = Maps.difference(defaultConfig, userConfig);
System.out.println("\nConfiguration Analysis:");
System.out.println("Common settings: " + configDiff.entriesInCommon());
System.out.println("Changed settings: " + configDiff.entriesDiffering());
System.out.println("Default only: " + configDiff.entriesOnlyOnLeft());
System.out.println("User only: " + configDiff.entriesOnlyOnRight());
// Merge strategy
Map<String, String> merged = new HashMap<>(defaultConfig);
merged.putAll(userConfig); // User config overrides defaults
System.out.println("Merged config: " + merged);
}
static class Person {
private final int id;
private final String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() { return id; }
public String getName() { return name; }
@Override
public String toString() {
return name + "(" + id + ")";
}
}
}
Advanced Collection Features
Range and RangeSet
import com.google.common.collect.*;
import java.util.*;
public class RangeExample {
public static void main(String[] args) {
// 1. Creating ranges
Range<Integer> closedRange = Range.closed(1, 10); // [1, 10]
Range<Integer> openRange = Range.open(1, 10); // (1, 10)
Range<Integer> closedOpen = Range.closedOpen(1, 10); // [1, 10)
Range<Integer> openClosed = Range.openClosed(1, 10); // (1, 10]
System.out.println("Closed [1,10]: " + closedRange);
System.out.println("Open (1,10): " + openRange);
System.out.println("Contains 5 in closed: " + closedRange.contains(5));
System.out.println("Contains 1 in open: " + openRange.contains(1));
// 2. Range operations
Range<Integer> range1 = Range.closed(1, 5);
Range<Integer> range2 = Range.closed(3, 8);
System.out.println("\nRange Operations:");
System.out.println("Range1: " + range1);
System.out.println("Range2: " + range2);
System.out.println("Union: " + range1.span(range2));
System.out.println("Intersection: " + range1.intersection(range2));
System.out.println("Is connected: " + range1.isConnected(range2));
// 3. RangeSet
RangeSet<Integer> rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closed(1, 10));
rangeSet.add(Range.closed(15, 20));
rangeSet.add(Range.closed(5, 12)); // Merges with first range
System.out.println("\nRangeSet: " + rangeSet);
System.out.println("Contains 8: " + rangeSet.contains(8));
System.out.println("Contains 13: " + rangeSet.contains(13));
System.out.println("Range containing 8: " + rangeSet.rangeContaining(8));
// 4. Practical example - age groups
RangeSet<Integer> ageGroups = TreeRangeSet.create();
ageGroups.add(Range.closedOpen(0, 13)); // Child
ageGroups.add(Range.closedOpen(13, 20)); // Teen
ageGroups.add(Range.closedOpen(20, 65)); // Adult
ageGroups.add(Range.atLeast(65)); // Senior
System.out.println("\nAge Groups:");
int[] testAges = {5, 15, 25, 70};
for (int age : testAges) {
System.out.println("Age " + age + ": " +
(ageGroups.contains(age) ? "Valid" : "Invalid"));
}
// 5. Complement
RangeSet<Integer> complement = rangeSet.complement();
System.out.println("\nComplement of rangeSet: " + complement);
}
}
Ordering and Comparison
import com.google.common.collect.*;
import java.util.*;
import java.util.Comparator;
public class OrderingExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Charlie", "Alice", "Bob", "David");
// 1. Natural ordering
Ordering<String> natural = Ordering.natural();
List<String> sortedNatural = natural.sortedCopy(names);
System.out.println("Natural order: " + sortedNatural);
// 2. Reverse ordering
Ordering<String> reverse = natural.reverse();
List<String> sortedReverse = reverse.sortedCopy(names);
System.out.println("Reverse order: " + sortedReverse);
// 3. Custom ordering
Ordering<String> byLength = new Ordering<String>() {
@Override
public int compare(String left, String right) {
return Integer.compare(left.length(), right.length());
}
};
List<String> sortedByLength = byLength.sortedCopy(names);
System.out.println("By length: " + sortedByLength);
// 4. Compound ordering
Ordering<String> byLengthThenNatural = byLength.compound(natural);
List<String> compoundSorted = byLengthThenNatural.sortedCopy(names);
System.out.println("By length then natural: " + compoundSorted);
// 5. Null handling
List<String> namesWithNull = Arrays.asList("Charlie", null, "Alice", "Bob", null);
Ordering<String> nullsFirst = natural.nullsFirst();
Ordering<String> nullsLast = natural.nullsLast();
System.out.println("Nulls first: " + nullsFirst.sortedCopy(namesWithNull));
System.out.println("Nulls last: " + nullsLast.sortedCopy(namesWithNull));
// 6. Min and max
String shortest = byLength.min(names);
String longest = byLength.max(names);
System.out.println("Shortest: " + shortest);
System.out.println("Longest: " + longest);
// 7. Greatest/Lessest of
List<String> top3Longest = byLength.greatestOf(names, 3);
List<String> bottom2Shortest = byLength.leastOf(names, 2);
System.out.println("Top 3 longest: " + top3Longest);
System.out.println("Bottom 2 shortest: " + bottom2Shortest);
// Practical example - student sorting
List<Student> students = Arrays.asList(
new Student("Alice", 85, 20),
new Student("Bob", 92, 22),
new Student("Charlie", 78, 19),
new Student("David", 92, 21)
);
// Sort by grade descending, then by age ascending
Ordering<Student> byGrade = Ordering.natural().reverse().onResultOf(Student::getGrade);
Ordering<Student> byAge = Ordering.natural().onResultOf(Student::getAge);
Ordering<Student> compound = byGrade.compound(byAge);
List<Student> sortedStudents = compound.sortedCopy(students);
System.out.println("\nStudents sorted by grade then age:");
sortedStudents.forEach(s -> System.out.println(s));
}
static class Student {
private final String name;
private final int grade;
private final int age;
public Student(String name, int grade, int age) {
this.name = name;
this.grade = grade;
this.age = age;
}
public String getName() { return name; }
public int getGrade() { return grade; }
public int getAge() { return age; }
@Override
public String toString() {
return String.format("%s (Grade: %d, Age: %d)", name, grade, age);
}
}
}
Best Practices and Performance
import com.google.common.collect.*;
import java.util.*;
public class BestPractices {
// 1. Prefer immutable collections for constants and shared data
public static final ImmutableList<String> CONSTANT_LIST =
ImmutableList.of("value1", "value2", "value3");
// 2. Use appropriate collection types
public static class CollectionChoices {
// Use Multiset for counting
public Multiset<String> countWords(String text) {
Multiset<String> wordCount = HashMultiset.create();
for (String word : text.split("\\s+")) {
wordCount.add(word.toLowerCase());
}
return wordCount;
}
// Use Multimap for one-to-many relationships
public Multimap<String, String> groupByCategory(List<Item> items) {
Multimap<String, String> categorized = ArrayListMultimap.create();
for (Item item : items) {
categorized.put(item.getCategory(), item.getName());
}
return categorized;
}
// Use BiMap for two-way lookup
public BiMap<String, Integer> createLookupTable(List<String> items) {
BiMap<String, Integer> lookup = HashBiMap.create();
for (int i = 0; i < items.size(); i++) {
lookup.put(items.get(i), i);
}
return lookup;
}
}
// 3. Efficient collection creation
public static class EfficientCollections {
// Know the expected size in advance
public List<String> createSizedList(int expectedSize) {
return Lists.newArrayListWithExpectedSize(expectedSize);
}
// Use builders for complex immutable collections
public ImmutableMap<String, String> buildComplexMap() {
return ImmutableMap.<String, String>builder()
.put("key1", "value1")
.put("key2", "value2")
.put("key3", "value3")
.build();
}
}
// 4. Avoid common pitfalls
public static class Pitfalls {
// Don't modify collections during iteration
public void safeModification(List<String> list) {
// Instead of modifying during iteration, use:
List<String> toRemove = new ArrayList<>();
for (String item : list) {
if (shouldRemove(item)) {
toRemove.add(item);
}
}
list.removeAll(toRemove);
}
// Be careful with lazy transformations
public void carefulWithTransform(List<Integer> numbers) {
List<String> strings = Lists.transform(numbers, String::valueOf);
// The transformation is applied on each access
// Consider copying if you need to access multiple times
List<String> copiedStrings = ImmutableList.copyOf(strings);
}
private boolean shouldRemove(String item) {
return item.startsWith("remove");
}
}
static class Item {
private final String category;
private final String name;
public Item(String category, String name) {
this.category = category;
this.name = name;
}
public String getCategory() { return category; }
public String getName() { return name; }
}
public static void main(String[] args) {
BestPractices practices = new BestPractices();
// Demonstrate best practices
String text = "hello world hello java world programming";
Multiset<String> wordCount = new CollectionChoices().countWords(text);
System.out.println("Word counts: " + wordCount);
// Show efficient collection usage
List<String> sizedList = new EfficientCollections().createSizedList(1000);
System.out.println("Created list with expected size 1000");
// Constants usage
System.out.println("Constant list: " + CONSTANT_LIST);
}
}
Guava's collection utilities provide powerful, efficient, and well-tested alternatives to Java's standard collections. They help write cleaner, more maintainable code while providing additional functionality not available in the JDK.