Exploring Google Guava
By Dan Lewis, OCI Senior Software Engineer
April 2010
Introduction
Google recently announced the public availability of Guava, a Java utility library previously available only internally at Google. Guava provides building blocks that build on the existing Java libraries and result in productivity aids for Java programmers. Guava also serves as an example of good Java coding idioms. Guava subsumes the Google Collections library and adds additional packages covering general-purpose utilities, input/output, primitives, and concurrency. My previous Java News Brief articles about Google Collections are listed in the references section at the end of this article. This article will cover almost every Class and Interface added to Google Guava since the Google Collections 1.0 release in a "definition list" format, grouped by package. Selected classes are utilized in demonstration examples. Because Google Guava, as of this writing, is distributed via Subversion repository access only, instructions are included at the end of the article for obtaining and building your own copy.
The Guava Packages
There are six Java packages in the Guava library:
- 1.
com.google.common.annotations
- annotations used by the other packages
- 2.
com.google.common.base
- common code used by the other packages, including all of the former Google Collections Library and some additional classes
- 3.
com.google.common.collect
- extensions of the Java collections framework, including all of the former Google Collections library and some additional classes
- 4.
com.google.common.io
- utility classes used for easing input/output
- 5.
com.google.common.primitives
- utility classes used when working with Java primitive types
- 6.
com.google.common.util.concurrent
- utility classes used when doing concurrent programming
Guava Package 2: com.google.common.base
This package was included with Google Collections 1.0. Since that release, the following classes, interfaces, and enums are new:
CaseFormat
- This enum defines and converts between some naming conventions commonly used in Java and C++ source code:
LOWER_HYPHEN
,LOWER_UNDERSCORE
,LOWER_CAMEL
,UPPER_CAMEL
, andUPPER_UNDERSCORE
CharMatcher
- Matches primitive
char
s andCharSequence
objects. This is used heavily in the Splitter class described later. Charsets
- Provides constant definitions for the standard Charset objects, so you don't need to use String constants in your code to obtain them anymore.
Defaults
- Contains default values for built-in Java types.
Service
- This interface generalizes the concept of a service that can be started and stopped via asynchronous method calls and whose state can be inspected in a thread-safe way.
Splitter
- This class is a utility class for splitting
String
s based on various conditions, the natural complement to Joiner from Google Collections and the spiritual successor tostrtok()
andStringTokenizer
. In the example below, observe that I start with the static methodon()
and then call "builder" methods to get the Splitter we want before finally callingsplit()
. I use thetrimResults()
andomitEmptyStrings()
builder options to obtain the desired results.
- package com.ociweb.jnb.apr2010;
-
- import com.google.common.base.Splitter;
-
- public class ExerciseSplitter {
- public static void main(String[] args) {
- final String input = "Alpha, Bravo,,Charlie ,Delta,Echo";
- printWords(Splitter.on(',').split(input));
- System.out.println();
- printWords(Splitter.on(',').trimResults().split(input));
- System.out.println();
- printWords(Splitter.on(',').trimResults().omitEmptyStrings().split(input));
- }
-
- private static void printWords(Iterable<String> words) {
- for (String word : words) {
- System.out.printf("[%s]\n", word);
- }
- }
- }
And the output is:
[Alpha]
[ Bravo]
[]
[Charlie ]
[Delta]
[Echo]
[Alpha]
[Bravo]
[]
[Charlie]
[Delta]
[Echo]
[Alpha]
[Bravo]
[Charlie]
[Delta]
[Echo]
Throwables
- This is a utility class that contains static methods for dealing with Exceptions (Throwables).
Thepropagate()
method (demonstrated below) will throw the argument Throwable unchanged if it is unchecked (i.e. RuntimeException or Error) or wrap it in a RuntimeException and throw it. This is useful if you are doing several operations that might throw exceptions but do not want to force callers to deal with checked exceptions.
- package com.ociweb.jnb.apr2010;
-
- import com.google.common.base.Throwables;
-
- import java.io.InputStream;
- import java.net.URL;
-
- public class ExerciseThrowables {
- public static void main(String[] args) {
- try {
- URL url = new URL("http://ociweb.com");
- final InputStream in = url.openStream();
- // read from the input stream
- in.close();
- } catch (Throwable t) {
- throw Throwables.propagate(t);
- }
- }
- }
Guava Package 3: com.google.common.collect
This package was included with Google Collections 1.0. Since that release, the following classes and interfaces are new:
ComparisonChain
- This is a utility class that is helpful for implementing the Comparable interface, e.g., the
compareTo()
method.
Recall thatcompareTo()
contractually returns -1, 0, +1 as the argument is "less", "equal", or "greater" than "this".
- package com.ociweb.jnb.apr2010;
-
- import com.google.common.collect.ComparisonChain;
- import com.google.common.collect.Sets;
-
- import java.util.Set;
-
- public class ExerciseComparisonChain {
-
- static class Person implements Comparable {
- private int birthMonth;
- private int birthDayOfWeek;
- private int birthYear;
- private String firstName;
-
- Person(String firstName, int birthMonth, int birthDayOfWeek, int birthYear) {
- this.birthMonth = birthMonth;
- this.birthDayOfWeek = birthDayOfWeek;
- this.birthYear = birthYear;
- this.firstName = firstName;
- }
-
- public int compareTo(Object o) {
- Person other = (Person) o;
- return ComparisonChain.start().
- compare(birthYear, other.birthYear).
- compare(birthMonth, other.birthMonth).
- compare(birthDayOfWeek, other.birthDayOfWeek).
- compare(firstName, other.firstName).
- result();
- }
- }
-
- public static void main(String[] args) {
- Set<Person> people = Sets.newTreeSet();
- people.add(new Person("Abigail", 4, 1, 1980));
- people.add(new Person("Courtney", 5, 2, 1981));
- people.add(new Person("Chastity", 5, 2, 1981));
- for (Person person : people) {
- System.out.println(person.firstName);
- }
- }
- }
And the output is:
Abigail
Chastity
Courtney
ImmutableAsList
- This class is used internally by
ImmutableCollection.asList()
ImmutableSortedAsList
- This class is used internally by
ImmutableSortedSet.asList()
LexicographicalOrdering
- This class is created by calling
Ordering.lexicographical()
. According to the Javadocs, it returns the "dictionary" ordering. Note that to use it, you need two lists, not one list of Strings.
- package com.ociweb.jnb.apr2010;
-
- import com.google.common.base.Joiner;
- import com.google.common.collect.Lists;
- import com.google.common.collect.Ordering;
-
- import java.util.Collections;
- import java.util.List;
-
- public class ExerciseLexicographicalOrdering {
- public static void main(String[] args) {
- List<String> names1 = Lists.newArrayList("whiskey", "tango", "golf");
- List<String> names2 = Lists.newArrayList("whiskey", "tango", "foxtrot");
- List<Iterable<String>> namesListList = Lists.newArrayList();
- namesListList.add(names1);
- namesListList.add(names2);
-
- final Ordering<Iterable<String>> lexOrd = Ordering.<String>natural().lexicographical();
-
- Collections.sort(namesListList, lexOrd);
-
- System.out.println(Joiner.on(',').join(namesListList));
-
- }
- }
And the output is:
[whiskey, tango, foxtrot],[whiskey, tango, golf]
Guava Package 4: com.google.common.io
This package wasn't part of Google Collections 1.0, so it is new to Guava. It contains the following classes and interfaces:
AppendableWriter
- This Writer wraps an
Appendable
. This is more flexible than StringWriter included with the JDK. ByteArrayDataInput
- This interface extends the
java.io.DataInput
interface, but the methods don't throw IOException. See static methodByteStreams.newDataInput()
. ByteArrayDataOutput
- This interface extends the
java.io.DataOutput
interface, but the methods don't throwIOException
. See static methodByteStreams.newDataOutput()
. ByteProcessor
- This interface is a callback expected by
Files.readBytes()
. ImplementprocessBytes()
to process bytes as they are received. Returntrue
to continue processing orfalse
otherwise. TheFiles
class is described later. ByteStreams
- Utility methods for reading, writing, copying, joining, and hashing InputStreams and OutputStreams (binary data).
CharStreams
- Utility methods for reading, writing, copying, joining, and hashing Readers and Writers (character data).
Closeables
- Utility methods for closing
Closeables
(e.g. Streams, Files, etc). In the example below,closeQuietly()
is used to close theOutputStream
and log any exceptions encountered to ajava.util.logging.Logger
.
- package com.ociweb.jnb.apr2010;
-
- import com.google.common.base.Throwables;
- import com.google.common.io.Closeables;
-
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.OutputStream;
-
- public class ExerciseCloseables {
- public static void main(String[] args) {
- final OutputStream outputStream = newTempFileOutputStream();
- Closeables.closeQuietly(outputStream);
- }
-
- private static OutputStream newTempFileOutputStream() {
- try {
- final File tmpFile = File.createTempFile(ExerciseCloseables.class.getName(), "tmp");
- return new FileOutputStream(tmpFile);
- } catch (IOException e) {
- throw Throwables.propagate(e);
- }
- }
- }
CountingInputStream
InputStream
that provides a getCount() method that returns number of bytes read as a long.CountingOutputStream
OutputStream
that provides a getCount() method that returns number of bytes read as a long.FileBackedOutputStream
- From the Javadocs: "An OutputStream that starts buffering to a byte array, but switches to file buffering once the data reaches a configurable size."
Files
- Utility methods for reading from, writing to, and manipulating files and directories.
Flushables
- Utility method for flushing
Flushables
(e.g. Streams, Files, etc). InputSupplier
- This parameterized interface is returned by several static utility methods in Files, ByteStreams, CharStreams, and Resources. Parameter T is usually an
InputStream
orReader
(or subclass). LimitInputStream
- This
InputStream
descendant is constructed with an integer that limits the maximum number of bytes that can be read. Attempting to read beyond returns -1, which typically means end of stream. LineBuffer
- This class is an implementation detail of
LineReader
. LineProcessor
- This interface is a callback passed as argument to
CharStreams.readlines()
,Files.readLines()
, andResources.readLines()
. TheprocessLine()
method is called back for each line, and you should return false to stop processing. Finally,getResult()
may be called to return the result of processing all the lines. The type of the return is type parameterT
. LineReader
- This is a more flexible version of BufferedReader that works for all implementers of
Readable
, not justReader
. For example,java.io.CharBuffer
subclasses are supported. MultiInputStream
- This class is used internally by
ByteStreams.join()
which allows multiple InputStreams to be "concatenated" or read serially, one after the other, as if they were one stream. MultiReader
- This class is used internally by
CharStreams.join()
which allows multiple Readers to be "concatenated" or read serially, one after the other, as if they were one reader. NullOutputStream
- This
OutputStream
subclass ignores all bytes. i.e. the "bit bucket". OutputSupplier
- This parameterized interface is returned by several static utility methods in Files, ByteStreams, CharStreams, and Resources. Parameter T is usually an
OutputStream
orWriter
(or subclass). PatternFilenameFilter
- This implementation of java.io.FilenameFilter uses a regular expression (passed to constructor) to filter files.
Resources
- The static utility methods in this class are useful for reading "Resources", or various non-code files that may be bundled with an application, e.g. data files, images, etc.
Guava Package 5: com.google.common.primitives
This package wasn't part of Google Collections 1.0 so is new to Guava. It contains the following classes and interfaces:
Booleans
,Bytes
,Chars
,Doubles
,Floats
,Ints
,Longs
,Shorts
,SignedBytes
,UnsignedBytes
- These classes provides static utility methods for manipulating individual primitives as well as primitives arrays, including searching, concatenating, wrapping and unwrapping. For the integral types (int, short, char byte) there are
checkedCast
andsaturatedCast
methods in the integral types that will convert a long to the given type.checkedCast
will throw an exception if the long is too large or too small, butsaturatedCast
will return the closest value. Primitives
- This class converts between primitive and wrapper meta-classes.
- package com.ociweb.jnb.apr2010;
-
- import com.google.common.primitives.Primitives;
-
- public class ExercisePrimitives {
- public static void main(String[] args) {
- System.out.println(Primitives.isWrapperType(Integer.class));
- System.out.println(Primitives.isWrapperType(int.class));
- final Class<Integer> clazz = Integer.class;
- System.out.println(clazz);
- final Class<Integer> unwrapped = Primitives.unwrap(clazz);
- System.out.println(unwrapped);
- final Class<Integer> unwrappedTwice = Primitives.unwrap(unwrapped);
- System.out.println(unwrappedTwice);
- final Class<Integer> rewrapped = Primitives.wrap(unwrappedTwice);
- System.out.println(rewrapped);
- }
- }
And the output is:
true
false
class java.lang.Integer
int
int
class java.lang.Integer
Guava Package 6: com.google.common.util.concurrent
This package wasn't part of Google Collections 1.0, so it is new to Guava. It contains the following classes and interfaces:
AbstractCheckedFuture
- This class wraps a
Future
, provided in the constructor, with thecheckedGet
methods specified inCheckedFuture
.
To extend this class you need to implement the mapException method that translates each of the three exceptions:InterruptedException
,CancellationException
, andExecutionException
.
Consider the following example that always maps all exceptions toCustomException
:
- package com.ociweb.jnb.apr2010;
-
- import com.google.common.util.concurrent.AbstractCheckedFuture;
- import com.google.common.util.concurrent.Futures;
- import com.google.common.util.concurrent.ListenableFuture;
-
- import java.util.concurrent.*;
-
- public class ExerciseAbstractCheckedFuture<V> extends AbstractCheckedFuture<V,
- ExerciseAbstractCheckedFuture.CustomException> {
-
- public ExerciseAbstractCheckedFuture(ListenableFuture<V> delegate) {
- super(delegate);
- }
-
- @Override
- protected CustomException mapException(Exception e) {
- return new CustomException(e);
- }
-
- public static class CustomException extends Exception {
- public CustomException(Throwable cause) {
- super(cause);
- }
- }
-
- public static void main(String[] args) {
- final Callable<Long> callable = new Callable<Long>() {
- public Long call() throws Exception {
- if (System.currentTimeMillis() % 2 == 0) {
- throw new RuntimeException();
- }
- return 100L;
- }
- };
- final ExecutorService executorService = Executors.newSingleThreadExecutor();
- final Future<Long> future = executorService.submit(callable);
- final ExerciseAbstractCheckedFuture<Long> checkedFuture =
- new ExerciseAbstractCheckedFuture<Long>(Futures.makeListenable(future));
- try {
- final long value = checkedFuture.checkedGet();
- System.out.println("value=" + value);
- } catch (CustomException e) {
- e.printStackTrace();
- }
- executorService.shutdown();
- }
- }
And the output is sometimes:
value=100
But other times it's a nasty stack trace starting with our CustomException
.
AbstractExecutionThreadService
- This abstract class would be a good starting point for building a custom threaded service. At a minimum, override the run method.
AbstractFuture
- This abstract class provides a starting point to implement a Future that is not based on Runnables. You simply override
get()
and you are on your way, with thread safety built-in. AbstractIdleService
- This abstract class can be used to implement services that do something on startup and shutdown, but otherwise don't do anything.
AbstractListenableFuture
- This is a base class that can be used to create a Future that also implements the
ListenableFuture
interface. See theFutures.makeListenable()
utility method. AbstractService
- This is a base class that can be used to create a
Service
by implementingdoStart()
anddoStop()
. Callables
- This class contains static utility methods for working with
Callables
. So far, the only method available isreturning()
, which always returns the same value immediately. CheckedFuture
- This sub-interface of
ListenableFuture
adds twogetChecked()
methods that return an Exception subclass of the parameterized type E. It also, through ListenableFuture supports notification when the value is available. DaemonThreadFactory
- This implementation of
ThreadFactory
wraps a givenThreadFactory
, then uses it to create threads, but before returning those threads callssetDaemon(true)
on them. ExecutionList
- This class pairs
Runnable
s withExecutor
s for later execution. This class is used by the implementations ofListenableFuture
interface in Guava but is public for broader use. Executors
- This class contains static utility methods mostly pertaining to
ThreadPoolExecutors
and their behavior during JVM shutdown.sameThreadExecutor()
creates anExecutorService
that runs all tasks in the same thread as execute and submit().daemonThreadFactory()
and variant can be used to create daemon threads. FakeTimeLimiter
- This is an implementation of
TimeLimiter
interface used for unit testing. ForwardingFuture
- This interface is implemented within
Futures.makeListenable()
to convert anyFuture
into aListenableFuture
. ForwardingService
- This Service delegates to another service. Was apparently used in an earlier iteration of Guava but not used as of this writing.
Futures
- Futures provides some utility methods for chaining and composing
Future
s with Functions. Also provided are themakeChecked(),
makeListenable(),
makeUninterruptable()
which can take a Future and make it checked, listenable, or uninterruptable, respectively. Could a FutureBuilder be in Guava's future? ListenableFuture
- This interface extends
Future
with anaddListener()
method that accepts both aRunnable
listener and anExecutor
on which to execute the listener. ListenableFutureTask
- This class extends FutureTask and adds the ability to register listeners.
NamingThreadFactory
- This is a ThreadFactory that can name threads by injecting the thread number into a given String pattern. This can reduce some boilerplate code
SimpleTimeLimiter
- This is the implementation of
TimeLimiter
provided with Guava. Execution of proxy target methods happen in anExecutorService
that is provided during construction. If none is provided by caller, aExecutors.newCachedThreadPool()
is used. TimeLimiter
- This creates a proxy class that limits duration of all method calls on the target object.
UncheckedTimeoutException
- This is an unchecked exception (extending
RuntimeException
makes it unchecked) that is thrown bySimpleTimeLimiter
when a timeout occured. UninterruptibleFuture
- An instance of this interface is returned by
Futures.makeUninterruptable()
. As expected, thisFuture
continues to execute even if interrupted. If interrupted during execution, it will set the interrupted flag on the current Thread after it has completed executing. ValueFuture
- This implementation of
ListenableFuture
can be used when result is already known, but something of typeListenableFuture
is needed. This is public but used internally in theFutures
class.
Licensing
Guava is provided under the Apache open source license. This is considered a non-viral open-source license meaning that you are not required to release your source code even if it consumes Google Guava. Note that open source doesn't mean open commits. The Google project leadership provides strict editorial control over what changes may be made to the library. Guava runs on Java 5 virtual machines and newer.
Obtain Guava
As of this writing, there is no pre-built jar file of Google Guava provided. The best way to get started with Guava is to check it out of the Google Code repository. You will need a Subversion (svn) client for your platform. The following command will check out the latest version:
svn checkout http://guava-libraries.googlecode.com/svn/trunk/ guava
What's in the box?
When you check out Guava, you'll see a root and three top level directories: javadoc, lib, and src.
- root directory
- Ant build.xml file, Apache 2.0 license, and IntelliJ IDEA project metadata
- javadoc
- the API documentation for Guava
- lib
- any 3rd-party libraries used by Guava, currently only
jsr305.jar
, the annotations for software defect detection - src
- the source code for Guava
The Ant build file.
To build Guava, set the JAVA5_HOME
environment variable. Run something like the following command, substituting the location of your Java installation:
set JAVA5_HOME=C:\Program Files\Java\jdk1.6.0_18
Support for at least Java 5 is enforced in the build script. I used Sun's Java 6 update 18 JDK to compile with impunity.
There are only three targets: compile (default), javadoc, and clean.
The compile target will dump everything into build/classes.
The javadoc target will output to build/javadoc. You can override the Java property "version" (default value "snapshot") to affect the window titles in the javadocs.
Apparently Google internally uses its own JDK, as evidenced by the metadata present in the javadocs. Inside the original allclasses-frame.html
you can see:
<!-- Generated by javadoc (build 1.6.0-google-internal) on Mon Jan 04 20:48:01 PST 2010 -->
but in my generated version you can see:
<!-- Generated by javadoc (build 1.6.0_18) on Sat Jan 16 11:11:38 CST 2010 -->
Conclusion
Guava can improve the productivity of any Java programmer who takes the time to learn the library. It does so in three ways.
First, it provides useful utilities to reduce the usage of error-prone code. For example, Charsets
provides constants required to be part of every JDK. Otherwise, one would be forced to use String literals.
Second, it builds on existing JDK libraries to give added flexibility and power. For example PatternFilenameFilter
, which brings together Regular expressions with java.io.FilenameFilter
. Further examples are Splitter
and Joiner
. Splitter corrects many of the deficiencies found in the String.split() family of methods and takes advantage of the Builder pattern. Joiner
, Splitter
's complement, eases the task of creating delimited Strings from String Arrays and Collections.
Finally, Guava is written by some very talented, yet pragmatic programmers and expresses the state-of-the-art of Java programming best practices and idioms.
For example, the builder pattern is used in the Splitter
class. This has a practical effect of making a very common task, tokenizing Strings, easier, yet more flexible than ever before.
Another example is Throwables. Many times a developer simply doesn't know what to do with a checked exception and prints it to stdout, and happily moves on. Logged statements are easily missed or eaten by application frameworks. Throwables force the issue by at once making the code cleaner and easier to follow, and simultaneously throwing an unchecked exception to a higher level which will usually cause an application to "fail fast". The net result is an application developer that observes an application error dialog immediately upon encountering a defect, rather than strange behavior at a point later in the program.
For these reasons, Google Guava will reward the developer that learns it, even if she is unable to utilize it in production code at this time. Because Guava is in a very early stage, the API's may change. So, if you choose to incorporate Guava into your application now, please recognize that you will likely need to modify code to support later snapshots and releases of Guava.
References
- [1] SETT Article (Feb 2009): Google Collections, Part 2
- [2] SETT Article (April 2008): Google Collections, Part 1
- [3] Guava Source Code
http://guava-libraries.googlecode.com/svn/trunk - [4] Guava: A Sketchy Overview - Kevin B9n.
http://guava-libraries.googlecode.com/files/Guava_a_sketchy_overview.pdf - [5] Guavadocs
http://guava-libraries.googlecode.com/svn/trunk/javadoc/index.html - [6] Guava libraries on Google Code
http://code.google.com/p/guava-libraries/ - [7] NATO phonetic alphabet
http://en.wikipedia.org/wiki/NATO_phonetic_alphabet - [8] Source Code for Examples
source code
Software Engineering Tech Trends (SETT) is a monthly newsletter featuring emerging trends in software engineering.