Comprehensive Java library providing essential utilities, immutable collections, caching, and concurrency tools for modern Java development.
—
Enhanced collection types and utilities including multimaps, multisets, tables, and powerful collection transformation operations. These utilities extend Java's collection framework with additional data structures and convenient operations.
Static utility methods for creating and manipulating collections.
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.Maps;
// Creating lists
List<String> arrayList = Lists.newArrayList("a", "b", "c");
List<String> linkedList = Lists.newLinkedList();
List<String> withCapacity = Lists.newArrayListWithCapacity(100);
// List operations
List<String> reversed = Lists.reverse(originalList);
List<List<String>> partitioned = Lists.partition(list, 3); // Split into chunks of 3
List<Character> chars = Lists.charactersOf("hello"); // ['h', 'e', 'l', 'l', 'o']
// Creating sets
Set<String> hashSet = Sets.newHashSet("a", "b", "c");
Set<String> linkedHashSet = Sets.newLinkedHashSet();
SortedSet<String> treeSet = Sets.newTreeSet();
// Set operations
Set<String> union = Sets.union(set1, set2);
Set<String> intersection = Sets.intersection(set1, set2);
Set<String> difference = Sets.difference(set1, set2);
Set<String> symmetricDifference = Sets.symmetricDifference(set1, set2);
// Power set (all possible subsets)
Set<Set<String>> powerSet = Sets.powerSet(ImmutableSet.of("a", "b", "c"));
// Creating maps
Map<String, Integer> hashMap = Maps.newHashMap();
Map<String, Integer> linkedHashMap = Maps.newLinkedHashMap();
SortedMap<String, Integer> treeMap = Maps.newTreeMap();
ConcurrentMap<String, Integer> concurrentMap = Maps.newConcurrentMap();Static utility methods for working with Iterable instances. Provides functional-style operations and convenient methods for common iterable manipulations.
import com.google.common.collect.Iterables;
import com.google.common.base.Predicate;
import com.google.common.base.Function;
// Size and containment
int size = Iterables.size(iterable);
boolean contains = Iterables.contains(iterable, element);
int frequency = Iterables.frequency(iterable, element);
boolean isEmpty = Iterables.isEmpty(iterable);
// Element access
String first = Iterables.getFirst(iterable, "default");
String last = Iterables.getLast(iterable);
String element = Iterables.get(iterable, 2); // Get element at index 2
String only = Iterables.getOnlyElement(iterable); // Expects exactly one element
// Filtering and transformation
Iterable<String> filtered = Iterables.filter(iterable, new Predicate<String>() {
public boolean apply(String s) { return s.startsWith("A"); }
});
Iterable<String> byType = Iterables.filter(objects, String.class);
Iterable<Integer> transformed = Iterables.transform(strings, new Function<String, Integer>() {
public boolean apply(String s) { return s.length(); }
});
// Finding elements
String found = Iterables.find(iterable, predicate);
String foundOrNull = Iterables.find(iterable, predicate, null);
Optional<String> tryFind = Iterables.tryFind(iterable, predicate);
int index = Iterables.indexOf(iterable, predicate);
// Predicates
boolean any = Iterables.any(iterable, predicate);
boolean all = Iterables.all(iterable, predicate);
// Combining and partitioning
Iterable<String> concatenated = Iterables.concat(iterable1, iterable2, iterable3);
Iterable<String> cycled = Iterables.cycle("A", "B", "C"); // Infinite cycle
Iterable<List<String>> partitioned = Iterables.partition(iterable, 3);
// Skipping and limiting
Iterable<String> skipped = Iterables.skip(iterable, 5);
Iterable<String> limited = Iterables.limit(iterable, 10);
// Conversion
String[] array = Iterables.toArray(iterable, String.class);
boolean added = Iterables.addAll(collection, iterable);
// Comparison and equality
boolean equal = Iterables.elementsEqual(iterable1, iterable2);
String string = Iterables.toString(iterable);
// Collection modification
boolean removed = Iterables.removeAll(iterable, elementsToRemove);
boolean retained = Iterables.retainAll(iterable, elementsToRetain);
boolean removedIf = Iterables.removeIf(iterable, predicate);Key Methods:
size(Iterable) - Returns number of elementscontains(Iterable, Object) - Tests if element is presentgetFirst(Iterable, T) / getLast(Iterable) - Get first/last elementsget(Iterable, int) - Get element at specific positiongetOnlyElement(Iterable) - Get single element (throws if not exactly one)filter(Iterable, Predicate) - Filter elements by predicatefilter(Iterable, Class) - Filter by typetransform(Iterable, Function) - Transform elementsfind(Iterable, Predicate) - Find first matching elementtryFind(Iterable, Predicate) - Find first matching element as Optionalany(Iterable, Predicate) / all(Iterable, Predicate) - Test predicatesconcat(Iterable...) - Concatenate multiple iterablescycle(T...) / cycle(Iterable) - Create cyclic iterablepartition(Iterable, int) - Split into fixed-size chunksskip(Iterable, int) - Skip first N elementslimit(Iterable, int) - Limit to first N elementselementsEqual(Iterable, Iterable) - Compare element-wise equalityStatic utility methods for working with Iterator instances. Companion to Iterables with iterator-specific operations.
import com.google.common.collect.Iterators;
// Size and advancement
int size = Iterators.size(iterator);
boolean advanced = Iterators.advance(iterator, 5); // Skip 5 elements
Iterator<T> consumed = Iterators.consumingIterator(iterator);
// Element access
T next = Iterators.getNext(iterator, defaultValue);
T last = Iterators.getLast(iterator);
T only = Iterators.getOnlyElement(iterator);
// Filtering and transformation
Iterator<String> filtered = Iterators.filter(iterator, predicate);
Iterator<String> byType = Iterators.filter(objects, String.class);
Iterator<Integer> transformed = Iterators.transform(strings, function);
// Finding
T found = Iterators.find(iterator, predicate);
Optional<T> tryFind = Iterators.tryFind(iterator, predicate);
int index = Iterators.indexOf(iterator, predicate);
// Predicates
boolean any = Iterators.any(iterator, predicate);
boolean all = Iterators.all(iterator, predicate);
// Combining
Iterator<T> concatenated = Iterators.concat(iter1, iter2, iter3);
Iterator<T> cycled = Iterators.cycle(list); // Infinite cycle over list
Iterator<List<T>> partitioned = Iterators.partition(iterator, batchSize);
// Limiting and peeking
Iterator<T> limited = Iterators.limit(iterator, maxElements);
PeekingIterator<T> peeking = Iterators.peekingIterator(iterator);
T peeked = peeking.peek(); // Look at next without consuming
// Conversion
List<T> list = Iterators.toList(iterator);
T[] array = Iterators.toArray(iterator, clazz);
boolean added = Iterators.addAll(collection, iterator);
// Unmodifiable wrappers
Iterator<T> unmodifiable = Iterators.unmodifiableIterator(iterator);
ListIterator<T> unmodifiableList = Iterators.unmodifiableListIterator(listIterator);
// Comparison
boolean equal = Iterators.elementsEqual(iter1, iter2);
String string = Iterators.toString(iterator);
// Collection modification
int removed = Iterators.removeAll(iterator, elementsToRemove);
int retained = Iterators.retainAll(iterator, elementsToRetain);
boolean removedIf = Iterators.removeIf(iterator, predicate);Key Methods:
size(Iterator) - Count remaining elements (consumes iterator)advance(Iterator, int) - Skip N elementsgetNext(Iterator, T) - Get next element with defaultgetLast(Iterator) - Get last element (consumes iterator)filter(Iterator, Predicate) - Filter by predicatetransform(Iterator, Function) - Transform elementsfind(Iterator, Predicate) - Find first matching elementconcat(Iterator...) - Concatenate multiple iteratorscycle(Iterable) - Create infinite cycling iteratorpartition(Iterator, int) - Batch elements into listslimit(Iterator, int) - Limit number of elementspeekingIterator(Iterator) - Wrap to support peekingconsumingIterator(Iterator) - Remove elements as they're accessedtoList(Iterator) / toArray(Iterator, Class) - Convert to collectionsA collection that maps keys to multiple values, similar to Map<K, Collection<V>> but with a cleaner API.
import com.google.common.collect.Multimap;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.LinkedHashMultimap;
// Creating multimaps
ListMultimap<String, String> listMultimap = ArrayListMultimap.create();
SetMultimap<String, String> setMultimap = HashMultimap.create();
SetMultimap<String, String> orderedMultimap = LinkedHashMultimap.create();
// Adding values
listMultimap.put("fruits", "apple");
listMultimap.put("fruits", "banana");
listMultimap.put("fruits", "apple"); // Duplicates allowed in ListMultimap
listMultimap.put("vegetables", "carrot");
// Retrieving values
Collection<String> fruits = listMultimap.get("fruits"); // ["apple", "banana", "apple"]
boolean hasFruit = listMultimap.containsKey("fruits"); // true
boolean hasApple = listMultimap.containsValue("apple"); // true
boolean hasEntry = listMultimap.containsEntry("fruits", "apple"); // true
// Bulk operations
listMultimap.putAll("colors", Arrays.asList("red", "green", "blue"));
listMultimap.removeAll("fruits"); // Removes all values for key
listMultimap.replaceValues("vegetables", Arrays.asList("carrot", "potato"));
// Views
Set<String> keys = listMultimap.keySet(); // All keys
Collection<String> values = listMultimap.values(); // All values
Collection<Map.Entry<String, String>> entries = listMultimap.entries(); // All key-value pairs
Map<String, Collection<String>> mapView = listMultimap.asMap(); // Map view
// Size operations
int size = listMultimap.size(); // Total number of key-value pairs
int keyCount = listMultimap.keySet().size(); // Number of distinct keys
boolean empty = listMultimap.isEmpty();Multimap Implementations:
ArrayListMultimap - Backed by ArrayList for each key (allows duplicate values, preserves order)HashMultimap - Backed by HashSet for each key (no duplicate values, no order guarantees)LinkedHashMultimap - Backed by LinkedHashSet for each key (no duplicates, preserves insertion order)TreeMultimap - Backed by TreeSet for each key (sorted values, no duplicates)A collection that allows duplicate elements and provides count-based operations.
import com.google.common.collect.Multiset;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.TreeMultiset;
// Creating multisets
Multiset<String> multiset = HashMultiset.create();
Multiset<String> sorted = TreeMultiset.create();
// Adding elements with counts
multiset.add("apple"); // count: 1
multiset.add("apple", 5); // count: 6
multiset.setCount("banana", 3); // Set absolute count
// Counting elements
int appleCount = multiset.count("apple"); // 6
int totalSize = multiset.size(); // Total number of elements (including duplicates)
int distinctSize = multiset.elementSet().size(); // Number of distinct elements
// Removing elements
multiset.remove("apple"); // count: 5
multiset.remove("apple", 2); // count: 3
multiset.setCount("apple", 0); // Remove completely
// Views
Set<String> distinctElements = multiset.elementSet(); // Unique elements
Set<Multiset.Entry<String>> entries = multiset.entrySet(); // Element-count pairs
// Iterating with counts
for (Multiset.Entry<String> entry : multiset.entrySet()) {
String element = entry.getElement();
int count = entry.getCount();
System.out.println(element + " appears " + count + " times");
}
// Multiset operations using Multisets utility class
import com.google.common.collect.Multisets;
Multiset<String> intersection = Multisets.intersection(multiset1, multiset2);
Multiset<String> union = Multisets.union(multiset1, multiset2);
boolean containsAll = Multisets.containsOccurrences(multiset1, multiset2);A bidirectional map that maintains both key-to-value and value-to-key mappings, ensuring that values are unique.
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
// Creating BiMap
BiMap<String, Integer> bimap = HashBiMap.create();
// Adding entries (values must be unique)
bimap.put("one", 1);
bimap.put("two", 2);
bimap.put("three", 3);
// Forward mapping
Integer value = bimap.get("one"); // 1
// Inverse mapping
BiMap<Integer, String> inverse = bimap.inverse();
String key = inverse.get(1); // "one"
// Forced put (removes existing mappings)
bimap.forcePut("four", 1); // Removes "one" -> 1 mapping
// Handling duplicate values
try {
bimap.put("four", 2); // Throws IllegalArgumentException (2 already mapped to "two")
} catch (IllegalArgumentException e) {
// Handle duplicate value
}A collection similar to Map<R, Map<C, V>> but with a cleaner API for two-dimensional data.
import com.google.common.collect.Table;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.TreeBasedTable;
import com.google.common.collect.ArrayTable;
// Creating tables
Table<String, String, Integer> table = HashBasedTable.create();
Table<String, String, Integer> sortedTable = TreeBasedTable.create();
// Adding data (row, column, value)
table.put("John", "Math", 95);
table.put("John", "Science", 87);
table.put("Jane", "Math", 92);
table.put("Jane", "Science", 94);
// Retrieving data
Integer johnMath = table.get("John", "Math"); // 95
boolean hasEntry = table.contains("John", "Math"); // true
boolean hasRow = table.containsRow("John"); // true
boolean hasColumn = table.containsColumn("Math"); // true
// Row and column views
Map<String, Integer> johnScores = table.row("John"); // {Math=95, Science=87}
Map<String, Integer> mathScores = table.column("Math"); // {John=95, Jane=92}
// All data views
Set<String> students = table.rowKeySet(); // [John, Jane]
Set<String> subjects = table.columnKeySet(); // [Math, Science]
Collection<Integer> allScores = table.values(); // [95, 87, 92, 94]
Set<Table.Cell<String, String, Integer>> cells = table.cellSet();
// Size and operations
int size = table.size(); // 4
table.remove("John", "Math");
table.clear();
// Array-based table (for dense data with known row/column sets)
List<String> students = Arrays.asList("John", "Jane", "Bob");
List<String> subjects = Arrays.asList("Math", "Science", "History");
Table<String, String, Integer> arrayTable = ArrayTable.create(students, subjects);Immutable ranges of Comparable values with various boundary types.
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;
// Creating ranges
Range<Integer> open = Range.open(1, 5); // (1, 5) - excludes endpoints
Range<Integer> closed = Range.closed(1, 5); // [1, 5] - includes endpoints
Range<Integer> openClosed = Range.openClosed(1, 5); // (1, 5]
Range<Integer> closedOpen = Range.closedOpen(1, 5); // [1, 5)
// Unbounded ranges
Range<Integer> atLeast = Range.atLeast(5); // [5, +∞)
Range<Integer> lessThan = Range.lessThan(5); // (-∞, 5)
Range<Integer> greaterThan = Range.greaterThan(5); // (5, +∞)
Range<Integer> atMost = Range.atMost(5); // (-∞, 5]
Range<Integer> all = Range.all(); // (-∞, +∞)
// Range operations
boolean contains = closed.contains(3); // true
boolean encloses = Range.closed(1, 10).encloses(Range.closed(2, 8)); // true
// Range intersection and span
Range<Integer> intersection = Range.closed(1, 5).intersection(Range.closed(3, 7)); // [3, 5]
Range<Integer> span = Range.closed(1, 3).span(Range.closed(5, 7)); // [1, 7]
// Range sets - collection of non-overlapping ranges
RangeSet<Integer> rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closed(1, 10));
rangeSet.add(Range.closed(15, 20));
rangeSet.remove(Range.open(5, 7));
// Result: [1, 5] ∪ [7, 10] ∪ [15, 20]
boolean inSet = rangeSet.contains(6); // false
RangeSet<Integer> complement = rangeSet.complement();Powerful utilities for working with Iterable and Iterator objects.
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.base.Predicate;
import com.google.common.base.Function;
// Concatenation
Iterable<String> combined = Iterables.concat(list1, list2, list3);
// Transformation
Iterable<Integer> lengths = Iterables.transform(strings, new Function<String, Integer>() {
public Integer apply(String input) {
return input.length();
}
});
// Filtering
Predicate<String> notEmpty = new Predicate<String>() {
public boolean apply(String input) {
return !input.isEmpty();
}
};
Iterable<String> filtered = Iterables.filter(strings, notEmpty);
// Finding elements
String first = Iterables.find(strings, notEmpty); // First matching element
Optional<String> found = Iterables.tryFind(strings, notEmpty); // Optional result
// Size and emptiness
int size = Iterables.size(iterable); // Works with any Iterable
boolean empty = Iterables.isEmpty(iterable);
// Element access
String first = Iterables.getFirst(strings, "default");
String last = Iterables.getLast(strings, "default");
String third = Iterables.get(strings, 2, "default"); // Index-based access
// Limiting and skipping
Iterable<String> limited = Iterables.limit(strings, 10); // First 10 elements
Iterable<String> skipped = Iterables.skip(strings, 5); // Skip first 5 elements
// Cycling and partitioning
Iterable<String> cycled = Iterables.cycle(strings); // Infinite repetition
Iterable<List<String>> partitioned = Iterables.partition(strings, 3); // Groups of 3
// Similar operations available for Iterators
Iterator<String> concatenated = Iterators.concat(iter1, iter2);
Iterator<Integer> transformed = Iterators.transform(stringIter, lengthFunction);
Iterator<String> filtered2 = Iterators.filter(stringIter, predicate);Advanced map operations including transformations, filtering, and specialized map types.
import com.google.common.collect.Maps;
import com.google.common.collect.MapDifference;
// Map transformation
Map<String, String> upperCaseKeys = Maps.transformKeys(originalMap,
new Function<String, String>() {
public String apply(String key) {
return key.toUpperCase();
}
});
Map<String, Integer> lengthValues = Maps.transformValues(stringMap,
new Function<String, Integer>() {
public Integer apply(String value) {
return value.length();
}
});
// Map filtering
Map<String, Integer> filteredByKeys = Maps.filterKeys(map,
new Predicate<String>() {
public boolean apply(String key) {
return key.startsWith("a");
}
});
Map<String, Integer> filteredByValues = Maps.filterValues(map,
new Predicate<Integer>() {
public boolean apply(Integer value) {
return value > 10;
}
});
// Map differences
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> diff = Maps.difference(left, right);
Map<String, Integer> entriesOnlyOnLeft = diff.entriesOnlyOnLeft(); // {c=3}
Map<String, Integer> entriesOnlyOnRight = diff.entriesOnlyOnRight(); // {d=5}
Map<String, Integer> entriesInCommon = diff.entriesInCommon(); // {a=1}
Map<String, MapDifference.ValueDifference<Integer>> entriesDiffering = diff.entriesDiffering(); // {b=(2, 4)}
// Creating map from properties
Properties props = new Properties();
Map<String, String> mapFromProps = Maps.fromProperties(props);import com.google.common.collect.Collections2;
import com.google.common.collect.Ordering;
// Collection transformation and filtering
Collection<Integer> lengths = Collections2.transform(strings, lengthFunction);
Collection<String> filtered = Collections2.filter(strings, notEmptyPredicate);
// Permutations
Collection<List<String>> permutations = Collections2.permutations(Arrays.asList("a", "b", "c"));
// Results in: [[a,b,c], [a,c,b], [b,a,c], [b,c,a], [c,a,b], [c,b,a]]
// Ordering (comparator utilities)
Ordering<String> byLength = Ordering.natural().onResultOf(new Function<String, Integer>() {
public Integer apply(String input) {
return input.length();
}
});
List<String> sorted = byLength.sortedCopy(strings);
String min = byLength.min(strings);
String max = byLength.max(strings);
List<String> greatest = byLength.greatestOf(strings, 3);
List<String> least = byLength.leastOf(strings, 3);
// Compound ordering
Ordering<Person> byAgeThemByName = Ordering.natural()
.onResultOf(new Function<Person, Integer>() {
public Integer apply(Person p) { return p.getAge(); }
})
.compound(Ordering.natural().onResultOf(new Function<Person, String>() {
public String apply(Person p) { return p.getName(); }
}));import com.google.common.collect.Multimaps;
import com.google.common.collect.Multisets;
import com.google.common.collect.Tables;
// Synchronized wrappers
Multimap<String, String> syncMultimap = Multimaps.synchronizedMultimap(multimap);
Multiset<String> syncMultiset = Multisets.synchronizedMultiset(multiset);
Table<String, String, Integer> syncTable = Tables.synchronizedTable(table);
// Unmodifiable views
Multimap<String, String> unmodifiableMultimap = Multimaps.unmodifiableMultimap(multimap);
Multiset<String> unmodifiableMultiset = Multisets.unmodifiableMultiset(multiset);
// Creating multimaps from existing maps
Map<String, String> existingMap = Maps.newHashMap();
Multimap<String, String> multimapFromMap = Multimaps.forMap(existingMap);
// Index operations - creating multimaps by indexing collections
List<String> words = Arrays.asList("apple", "banana", "apricot", "blueberry");
Multimap<Character, String> byFirstLetter = Multimaps.index(words,
new Function<String, Character>() {
public Character apply(String word) {
return word.charAt(0);
}
});
// Result: {a=[apple, apricot], b=[banana, blueberry]}Specialized collection implementations for specific use cases.
import com.google.common.collect.ArrayTable;
import com.google.common.collect.Collections2;
import com.google.common.collect.Ordering;
// Array-based table for dense data with known dimensions
List<String> rowKeys = Arrays.asList("Q1", "Q2", "Q3", "Q4");
List<String> columnKeys = Arrays.asList("Sales", "Marketing", "Engineering");
ArrayTable<String, String, Integer> quarterlyBudget = ArrayTable.create(rowKeys, columnKeys);
quarterlyBudget.put("Q1", "Sales", 100000);
quarterlyBudget.put("Q1", "Marketing", 50000);
quarterlyBudget.put("Q1", "Engineering", 200000);
// Efficient access by index
Integer q1Sales = quarterlyBudget.at(0, 0); // Q1 Sales budget
Map<String, Integer> q1Budget = quarterlyBudget.row("Q1");
// Collections2 utilities for collection transformation
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// Transform collection (creates a view, not a copy)
Collection<String> upperCase = Collections2.transform(names,
new Function<String, String>() {
public String apply(String name) {
return name.toUpperCase();
}
});
// Filter collection (creates a view, not a copy)
Collection<String> longNames = Collections2.filter(names,
new Predicate<String>() {
public boolean apply(String name) {
return name.length() > 4;
}
});
// Generate permutations
Collection<List<String>> permutations = Collections2.permutations(
Arrays.asList("A", "B", "C"));
// Results in all 6 permutations: [A,B,C], [A,C,B], [B,A,C], etc.
// Ordering utilities (enhanced comparator)
Ordering<String> byLength = Ordering.natural().onResultOf(
new Function<String, Integer>() {
public Integer apply(String s) {
return s.length();
}
});
// Use ordering for various operations
List<String> words = Arrays.asList("elephant", "cat", "hippopotamus", "dog");
List<String> sortedByLength = byLength.sortedCopy(words); // [cat, dog, elephant, hippopotamus]
String shortest = byLength.min(words); // "cat"
String longest = byLength.max(words); // "hippopotamus"
// Get top/bottom N elements
List<String> shortest3 = byLength.leastOf(words, 3); // [cat, dog, elephant]
List<String> longest2 = byLength.greatestOf(words, 2); // [hippopotamus, elephant]
// Null-safe ordering
Ordering<String> nullsFirst = Ordering.natural().nullsFirst();
Ordering<String> nullsLast = Ordering.natural().nullsLast();
// Compound ordering (multiple criteria)
Ordering<Person> byAgeThemName = Ordering.natural()
.onResultOf(new Function<Person, Integer>() {
public Integer apply(Person p) { return p.getAge(); }
})
.compound(Ordering.natural().onResultOf(new Function<Person, String>() {
public String apply(Person p) { return p.getName(); }
}));Additional Collection Classes:
ArrayTable<R,C,V> - Table implementation backed by a 2D array, optimized for dense dataCollections2 - Static utility methods for creating transformed views of collectionsOrdering<T> - Enhanced comparator with fluent API for sorting and selection operationsThis collections framework provides powerful abstractions that extend Java's standard collections with specialized data structures optimized for common use cases, comprehensive utility methods for transformation and manipulation, and convenient APIs that reduce boilerplate code while maintaining type safety and performance.
Install with Tessl CLI
npx tessl i tessl/maven-com-google-guava--guava