Guava Collection Utilities

Goals

Concepts

Library

Dependencies

Lesson

Java's collection interfaces have proved so popular that third parties have created utilities for working with collections, and even new implementations of the ADT interfaces. Notably Google's Guava library contains a wealth of collection utilities that are useful in day-to-day programming, bringing a level of convenience beyond that found in the standard Java collection utilities. The Guava collection utilities are found in the com.google.common.collect package. Some of the most important collection utilities in Guava are examined here, but there are just too many to cover in one lesson. You are encouraged to review the articles in the See Also section below for more complete coverage.

Guava Collection Utilities

Similar to Java's java.util.Collections class, Guava offers a com.google.common.collect.Collections2 class (so named to avoid conflicts with the Java version, even though they are in different packages) containing a few extra collection utilities. Most of Guava's utilities are found in separate classes, though. Some utilities are collected especially for lists, maps, sets, etc. (found in classes with just such those names). Many of the utilities consist of new ADT interfaces and their implementations.

Iterators and Iterables

The com.google.common.collect.Iterators and com.google.common.collect.Iterables classes are collections of utilities for working with Iterator<E> and Iterable<T>. Here are just a two examples:

concatenation
Guava provides Iterators.concat(Iterator<? extends T>... inputs) and Iterables.concat(Iterable<? extends T>... inputs) to concatenate (i.e. join the contents) of multiple iterators or iterables, respectively. For example, if you concatenate to iterators, it gives back and iterator that will iterate over the first iterable, and when the first is finished it will continue iterating over the second! The concatenation is lazy in that the concatenation happens in real time, as you iterate over the items. (The items from both iterators are not collected together up front in e.g. some collection.)
filtering
Methods Iterators.filter(Iterator<T> unfiltered, Predicate<? super T> predicate) and Iterables.filter(Iterable<T> unfiltered, Predicate<? super T> predicate) filter the results based upon the result of some predicate, or functional definition of what should be filtered. The Guava com.google.common.base.Predicate<T> interface is a strategy for determining whether each item should be included or filtered out, based upon the apply(T input) method. Java comes with its own java.util.function.Predicate<T> interface, which you will learn about in an upcoming lesson, so try not to use the Guava one unless you have to in order to work with Guava utilities.

Immutable Collections

You already know that you can make any of your collections immutable by using e.g. java.util.Collections.unmodifiableList(…), which wraps a mutable collection with an immutable decorator instance. Guava goes further by providing a an extensive group of collection classes that are already mutable. They provide additional static factory methods that make creating immutable collections a breeze. Here's an example of creating an immutable List<E>.

Creating a Guava ImmutableList using a static factory method.
final List<Foo> immutableList = ImmutableList.of(new Foo(1), new Foo(2), new Foo(3));

Builders

Many of Guava's collection classes come with builders. These are intermediate classes that accumulate the items you want to appear in a collection. The builder is mutable, allowing you to add items at will, until you finally call the build() method to produce the immutable set. Normally you will ask the immutable collection class itself for a builder via its builder() method, such as ImmutableSet.builder().

Building a Guava ImmutableSet using a builder.
final ImmutableSet.Builder<Foo> builder = ImmutableSet.builder();
builder.add(new Foo(1));
builder.add(new Foo(2));
builder.add(new Foo(3));
final Set<Foo> immutableSet = builder.build();

Multiset<E>

Guava adds a new collection type—a multiset. Similar to a set, a com.google.common.collect.MultiSet<E> doesn't keep track of order or indexes. But unlike a set, a multiset is able to keep track of multiple occurrences of equal items. Utilities are provided in com.google.common.collect.Multisets.

Although a Multiset<E> is not a Set<E>, it is a Collection<E> and as such allows adding elements via add(E) and checking for elements via contains(Object). Its additional methods relate primarily to determining the number or occurrences or count of each item in the multiset, as that is its primary distinction from Set<E>.

add(E element, int occurrences)
Adds a specific number of occurrences of a single element; equivalent to calling add(E) multiple times.
count(Object element)
Returns the number occurrences of a single element.
setCount(E element, int count)
Adds or removes occurrences of the given element to attain the indicated count.

Implementations

com.google.common.collect.HashMultiset<E>
A multiset based upon a hash table.
com.google.common.collect.LinkedHashMultiset<E>
Equivalent to a HashMultiset<E>, except that it also maintains a linked list to maintain iteration order.
com.google.common.collect.EnumMultiset<E>
Efficient and compact multiset implementation for enums.
com.google.common.collect.ImmutableMultiset<E>
An immutable multiset based upon a hash table.
Using a Guava HashMultiset.
final Multiset<String> multiset = HashMultiset.create();
multiset.add("abc");
multiset.add("foo");
multiset.add("def");
multiset.add("foo");
multiset.add("bar");
multiset.add("foo");
System.out.println(multiset.count("foo"));  //prints "3"

Multimap<K, V>

A multimap is another new abstract data type, similar to a map except that each key can be associated with more than one value. A com.google.common.collect.MultiMap<K, V> is not a subinterface of java.util.Map<K, V>, although you can retrieve a view of the multiset as a map using the Multimap.asMap() method as explained below. Utilities are provided in com.google.common.collect.Multimaps.

asMap()
Returns a view of the multiset as a Map<K, Collection<V>>. This map is a live view; modifying it will modifying the underlying multimap.
get(K key)
Returns a view of all the values associated with the indicated key as Collection<V>. This collection is a live view; modifying it will modifying the underlying multimap.
put(K key, V value)
Stores a key-value pair in the multimap. If a key already contains a value, this method may result in multiple values being associated with the same key.
size()
Returns the number of key-value pairs, including the values for keys with multiple values. Note that this is not necessarily the same as the number of keys.
values()
Returns a collection containing all the combined individual values, including any duplicates, that would be returned for each key. The number of values returned will be equal to size().

Implementations

com.google.common.collect.ArrayListMultimap<K, V>
A hash table-based map that uses an array-based list to store items associated with each key. This allows duplicate values to be associated with a key.
com.google.common.collect.HashMultimap<K, V>
A hash table-based map that uses a hash table for values. This prevents duplicate values from being associated with a single key. A more appropriate name might have been HashSetMultimap<K, V>.
com.google.common.collect.LinkedHashMultimap<K, V>
A hash table-based map that uses a hash table for values, but retains the insertion order.
com.google.common.collect.LinkedListMultimap<K, V>
A hash table-based map that uses a linked-list-based list to store items associated with each key. This allows duplicate values to be associated with a key.
com.google.common.collect.TreeMultimap<K, V>
A tree-based map that also uses a tree to store items associated with each key. Ordering is natural ordering or based on a supplied comparator.
com.google.common.collect.ImmutableMultimap<K, V>
An immutable multimap copy of data.
com.google.common.collect.ImmutableListMultimap<K, V>
An immutable multimap copy of data that stores values in a list.
com.google.common.collect.ImmutableSetMultimap<K, V>
An immutable multimap copy of data that stores values in a set.
Using a Guava HashMultiset.
final Multimap<String, String> colorThings = ArrayListMultimap.create();
colorThings.put("yellow", "sun");
colorThings.put("green", "grass");
colorThings.put("green", "moss");

System.out.println(colorThings.size());  //prints "3"
System.out.println(colorThings.get("green").size());  //prints "2"
//prints "yellow", "grass", and "moss" in some order
for(final String thing : colorThings.values()) {
  System.out.println(thing);
}

BiMap<K, V>

A bimap, represented by the Guava interface com.google.common.collect.BiMap<K, V> is a Map<K, V> that allows you not only to look up a value from a key, but also a key from a value in the opposite direction! The main method that allows this is BiMap.inverse(), which returns a Map<K, V> of the value-to-key mapping.

inverse()
Returns a Map<K, V> view of the value-to-key mapping—the reverse mapping of the original map. This map is a live view; modifying it will modifying the underlying bimap.

Implementations

com.google.common.collect.HashBiMap<K, V>
An unmodifiable BiMap<K, V>.
com.google.common.collect.EnumBiMap<K, V>
A bimap for enums as both keys and values, backed by two EnumMap<K, V> instances.
com.google.common.collect.EnumHashBiMap<K, V>
A bimap with an EnumMap<K, V> for the keys and a hash table for the values.
A bimap backed by two hash tables.
com.google.common.collect.ImmutableBiMap<K, V>
Using a Guava HashBiMap.
final BiMap<Integer, String> numberLabels = HashBiMap.create();
numberLabels.put(1, "one");
numberLabels.put(2, "two");
numberLabels.put(3, "three");
final String oneLabel = numberLabels.get(1); //retrieves "one"
final Integer threeNumber = numberLabels.inverse().get("three"); //retrieves 3

Table<R, C, V>

The com.google.common.collect.Table<R, C, V> interface represents a table abstract data type, and is a way essentially to associate values with two keys instead of with a single key as in a Map<K, V>. Thus rather than a single key, a Table<R, C, V> uses a separate row and a column. Analogous to a Map.Entry<K, V>, each value in a table is stored in a Table.Cell<R, C, V> instance. Here are some of the most useful table methods:

cellSet()
Returns a live set of all the cells in the table.
column(C columnKey)
Returns a view all the row-value mappings for a column.
contains(Object rowKey, Object columnKey)
Indicates whether the table has a cell value for the specified row and column.
get(Object rowKey, Object columnKey)
Retrieves the cell value for the given row and column, or null if there is no such mapping.
isEmpty()
A convenience method logically equivalent to checking size() == 0. Calling isEmpty() is preferred over checking the size, because the underlying implementation may have a more efficient way than retrieving the number of key-column-value associations.
row(R rowKey)
Retrieves a view of all the column-value mappings for a row.
size()
Returns the number of table cells

Implementations

com.google.common.collect.HashBasedTable<R, C, V>
A table implemented using hash tables.
com.google.common.collect.ImmutableTable<R, C, V>
An immutable table with builder.
com.google.common.collect.TreeBasedTable<R, C, V>
A table implemented using trees. Ordering is natural ordering or based on supplied comparators.
Using a Guava HashBasedTableMap.
final Table<String, String, Integer> studentHomeworkScores = HashBasedTable.create();
studentHomeworkScores.put("jane", "homework1", 100);
studentHomeworkScores.put("jane", "homework2", 80);
studentHomeworkScores.put("john", "homework1", 90);
studentHomeworkScores.put("john", "homework2", 90);
final Integer janeHomework2Score = studentHomeworkScores.get("jane", "homework2");  //retrieves 80

Review

In the Real World

Self Evaluation

Task

In your booker project, you should have already created an immutable collection of publications by first creating a mutable collection and wrapping it in an unmodifiable collection using Javas Collection utilities. Convert this immutable collection to instead use one of the Guava immutable ADT implementations, using that implementation's builder to fill the collection with publications before assigning it to your variable.

See Also

Resources