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.
There are six Java packages in the Guava library:
com.google.common.annotations
com.google.common.base
com.google.common.collect
com.google.common.io
com.google.common.primitives
com.google.common.util.concurrent
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
CharMatcher
chars and CharSequence objects. This is used heavily in the Splitter
class described later.
Charsets
Defaults
Service
Splitter
Strings 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]\n", word);
}
}
}
And the output is:
[Alpha] [ Bravo] [] [Charlie ] [Delta] [Echo] [Alpha] [Bravo] [] [Charlie] [Delta] [Echo] [Alpha] [Bravo] [Charlie] [Delta] [Echo]
Throwables
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();
// read from the input stream
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
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
ImmutableSortedAsList
LexicographicalOrdering
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
Appendable. This is more flexible than StringWriter included with the JDK.
ByteArrayDataInput
java.io.DataInput interface, but the methods don't throw IOException.
See static
method ByteStreams.newDataInput().
ByteArrayDataOutput
java.io.DataOutput interface, but the methods don't throw IOException.
See static
method ByteStreams.newDataOutput().
ByteProcessor
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
CharStreams
Closeables
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
Files
Flushables
Flushables (e.g. Streams, Files, etc).InputSupplier
InputStream or Reader (or subclass).
LimitInputStream
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
LineReader.
LineProcessor
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
Readable, not
just Reader.
For example, java.io.CharBuffer subclasses are supported.
MultiInputStream
ByteStreams.join() which allows multiple InputStreams to be
"concatenated" or
read serially, one after the other, as if they were one stream.
MultiReader
CharStreams.join() which allows multiple Readers to be
"concatenated" or
read serially, one after the other, as if they were one reader.
NullOutputStream
OutputStream subclass ignores all bytes. i.e. the "bit bucket".OutputSupplier
OutputStream or Writer (or subclass).
PatternFilenameFilter
Resources
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
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
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
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
AbstractFuture
get() and you are on your way, with thread safety built-in.
AbstractIdleService
AbstractListenableFuture
ListenableFuture
interface. See the Futures.makeListenable() utility method.
AbstractService
Service by implementing doStart()
and doStop().
Callables
Callables. So far, the only method
available is returning(), which always returns the same value immediately.
CheckedFuture
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
ThreadFactory wraps a given ThreadFactory, then uses it to
create threads, but before returning those threads calls setDaemon(true) on them.
ExecutionList
Runnables with Executors for later execution. This class is used by
the implementations of ListenableFuture interface in Guava but is public for broader use.
Executors
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
TimeLimiter interface used for unit testing.ForwardingFuture
Futures.makeListenable() to convert any Future
into a
ListenableFuture.
ForwardingService
Futures
Futures 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
Future with an addListener() method that accepts both a
Runnable listener and an Executor on which to execute the listener.
ListenableFutureTask
NamingThreadFactory
SimpleTimeLimiter
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
UncheckedTimeoutException
RuntimeException makes it unchecked) that is thrown
by SimpleTimeLimiter when a timeout occured.
UninterruptibleFuture
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
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.
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.
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.
When you check out Guava, you'll see a root and three top level directories: javadoc, lib, and src.
jsr305.jar, the annotations for software
defect detection
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 -->
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.