Exploring Google Guava

by

Dan Lewis, Senior Software Engineer

Object Computing, Inc. (OCI)

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:

com.google.common.annotations annotations used by the other packages com.google.common.base common code used by the other packages, including all of the former Google Collections Library and some additional classes com.google.common.collect extensions of the Java collections framework, including all of the former Google Collections library and some additional classes com.google.common.io utility classes used for easing input/output com.google.common.primitives utility classes used when working with Java primitive types com.google.common.util.concurrent utility classes used when doing concurrent programming

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, and UPPER_UNDERSCFORE CharMatcher Matches primitive char s and CharSequence 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 to strtok() and StringTokenizer. In the example below, observe that I start with the static method on() and then call "builder" methods to get the Splitter we want before finally calling split() . I use the trimResults() and omitEmptyStrings() 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]

", 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). The propagate() 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(); in.close(); } catch (Throwable t) { throw Throwables.propagate(t); } } }

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 that compareTo() 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]

com.google.common.io

This package wasn't part of Google Collections 1.0 so 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 method ByteStreams.newDataInput() . ByteArrayDataOutput This interface extends the java.io.DataOutput interface, but the methods don't throw IOException . See static method ByteStreams.newDataOutput() . ByteProcessor This interface is a callback expected by Files.readBytes() . Implement processBytes() to process bytes as they are received. Return true to continue processing or false otherwise. The Files 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 the OutputStream and log any exceptions encountered to a java.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 or Reader (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() , and Resources.readLines() . The processLine() 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 parameter T . LineReader This is a more flexible version of BufferedReader that works for all implementers of Readable , not just Reader . 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 or Writer (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.

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 and saturatedCast 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, but saturatedCast 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

com.google.common.util.concurrent

This package wasn't part of Google Collections 1.0 so is new to Guava. It contains the following classes and interfaces:

AbstractCheckedFuture This class wraps a Future , provided in the constructor, with the checkedGet methods specified in CheckedFuture . To extend this class you need to implement the mapException method that translates each of the three exceptions: InterruptedException , CancellationException , and ExecutionException . Consider the following example that always maps all exceptions to CustomException :

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 the Futures.makeListenable() utility method. AbstractService This is a base class that can be used to create a Service by implementing doStart() and doStop() . Callables This class contains static utility methods for working with Callables . So far, the only method available is returning() , which always returns the same value immediately. CheckedFuture This sub-interface of ListenableFuture adds two getChecked() 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 given ThreadFactory , then uses it to create threads, but before returning those threads calls setDaemon(true) on them. ExecutionList This class pairs Runnable s with Executor s for later execution. This class is used by the implementations of ListenableFuture 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 an ExecutorService 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 any Future into a ListenableFuture . 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 the makeChecked(), 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 an addListener() method that accepts both a Runnable listener and an Executor 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 an ExecutorService that is provided during construction. If none is provided by caller, a Executors.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 by SimpleTimeLimiter when a timeout occured. UninterruptibleFuture An instance of this interface is returned by Futures.makeUninterruptable() . As expected, this Future 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 type ListenableFuture is needed. This is public but used internally in the Futures 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

This will check out Google Guava library into the guava subdirectory. You can replace this part of the command with any directory you want.

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