1. Overview
Google Guava provides libraries with utilities that ease Java development. In this tutorial we will take a look to new functionality introduced in the Guava 18 release.
2. MoreObjects Utility Class
Guava 18 saw the addition of the MoreObjects class, which contains methods that do not have equivalents in java.util.Objects.
As of release 18, it only contains implementations of the toStringHelper method, which can be used to help you to build your own toString methods.
- toStringHelper(Class<?> clazz)
- toStringHelper(Object self)
- toStringHelper(String className)
Typically, toString() is used when you need to output some information about an object*.* Usually it should contain details about the current state of the object. By using one of the implementations of toStringHelper, you can easily build a useful toString() message.
Suppose we have a User object containing a few fields that need to be written when toString() is called. We can use the MoreObjects.toStringHelper(Object self) method to do this easily.
public class User {
private long id;
private String name;
public User(long id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("id", id)
.add("name", name)
.toString();
}
}
Here we have used toStringHelper(Object self) method. With this setup, we can create a sample user to see the output that results when calling toString().
User user = new User(12L, "John Doe");
String userState = user.toString();
// userState: User{ id=12,name=John Doe }
The other two implementations will return the same String if configured similarly:
toStringHelper(Class<?> clazz)
@Override
public String toString() {
return MoreObjects.toStringHelper(User.class)
.add("id", id)
.add("name", name)
.toString();
}
toStringHelper(String className)
@Override
public String toString() {
return MoreObjects.toStringHelper("User")
.add("id", id)
.add("name", name)
.toString();
}
The difference between these methods is evident if you call toString() on extensions of the User class. For example, if you have two kinds of Users: Administrator and Player, they would produce different output.
public class Player extends User {
public Player(long id, String name) {
super(id, name);
}
}
public class Administrator extends User {
public Administrator(long id, String name) {
super(id, name);
}
}
If you use toStringHelper(Object self) in the User class then your Player.toString() will return “*Player{id=12, name=John Doe}“. However, if you use toStringHelper(String className) or toStringHelper(Class<?> clazz), Player.toString() will return “User{id=12, name=John Doe}*“. The class name listed will be the parent class rather than the subclass.
3. New Methods in FluentIterable
3.1. Overview
FluentIterable is used to operate with Iterable instances in a chained fashion. Lets see how it can be used.
Suppose you have list of User objects, defined in the examples above, and you wish to filter that list to include only the users that are aged 18 or older.
List<User> users = new ArrayList<>();
users.add(new User(1L, "John", 45));
users.add(new User(2L, "Michelle", 27));
users.add(new User(3L, "Max", 16));
users.add(new User(4L, "Sue", 10));
users.add(new User(5L, "Bill", 65));
Predicate<User> byAge = user -> user.getAge() >= 18;
List<String> results = FluentIterable.from(users)
.filter(byAge)
.transform(Functions.toStringFunction())
.toList();
The resulting list will contain the information for John, Michelle, and Bill.
3.2. FluentIterable.of(E[])
With this method. you can create a FluentIterable from array of Object.
User[] usersArray = { new User(1L, "John", 45), new User(2L, "Max", 15) } ;
FluentIterable<User> users = FluentIterable.of(usersArray);
You can now use the methods provided in FluentIterable interface.
3.3. FluentIterable.append(E…)
You can create new FluentIterable from existing FluentIterable by appending more elements to it.
User[] usersArray = {new User(1L, "John", 45), new User(2L, "Max", 15)};
FluentIterable<User> users = FluentIterable.of(usersArray).append(
new User(3L, "Sue", 23),
new User(4L, "Bill", 17)
);
As expected, the size of the resultant FluentIterable is 4.
3.4. FluentIterable.append(Iterable<? extends E>)
This method behaves the same as the previous example, but allows you to add the entire contents of any existing implementation of Iterable to a FluentIterable.
User[] usersArray = { new User(1L, "John", 45), new User(2L, "Max", 15) };
List<User> usersList = new ArrayList<>();
usersList.add(new User(3L, "Diana", 32));
FluentIterable<User> users = FluentIterable.of(usersArray).append(usersList);
As expected, the size of the resultant FluentIterable is 3.
3.5. FluentIterable.join(Joiner)
The FluentIterable.join(…) method produces a String representing the entire contents of the FluentIterable, joined by a given String.
User[] usersArray = { new User(1L, "John", 45), new User(2L, "Max", 15) };
FluentIterable<User> users = FluentIterable.of(usersArray);
String usersString = users.join(Joiner.on("; "));
The usersString variable will contain the output of calling the toString() method on each element of the FluentIterable, separated by a “;”. The Joiner class provides several options for joining the strings.
4. Hashing.crc32c
A hash function is any function that can be used to map data of arbitrary size to data of a fixed size. It is used in many areas, such as cryptography and checking for errors in transmitted data.
The Hashing.crc32c method returns a HashFunction that implements the CRC32C algorithm.
int receivedData = 123;
HashCode hashCode = Hashing.crc32c().hashInt(receivedData);
// hashCode: 495be649
5. InetAddresses.decrement(InetAddress)
This method returns a new InetAddress that will be “one less” than its input.
InetAddress address = InetAddress.getByName("127.0.0.5");
InetAddress decrementedAddress = InetAddresses.decrement(address);
// decrementedAddress: 127.0.0.4
6. New Executors in MoreExecutors
6.1. Threading Review
In Java you can use multiple threads to execute work. For this purpose, Java has Thread and Runnable classes.
ConcurrentHashMap<String, Boolean> threadExecutions = new ConcurrentHashMap<>();
Runnable logThreadRun = () -> threadExecutions.put(Thread.currentThread().getName(), true);
Thread t = new Thread(logThreadRun);
t.run();
Boolean isThreadExecuted = threadExecutions.get("main");
As expected, isThreadExecuted will be true. Also you can see that this Runnable will run only in the main thread. If you want to use multiple threads, you can use different Executors for different purposes.
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(logThreadRun);
executorService.submit(logThreadRun);
executorService.shutdown();
Boolean isThread1Executed = threadExecutions.get("pool-1-thread-1");
Boolean isThread2Executed = threadExecutions.get("pool-1-thread-2");
// isThread1Executed: true
// isThread2Executed: true
In this example, all submitted work is executed in ThreadPool threads.
Guava provides different methods in its MoreExecutors class.
6.2. MoreExecutors.directExecutor()
This is a lightweight executor that can run tasks on the thread that calls the execute method.
Executor executor = MoreExecutors.directExecutor();
executor.execute(logThreadRun);
Boolean isThreadExecuted = threadExecutions.get("main");
// isThreadExecuted: true
6.3. MoreExecutors.newDirectExecutorService()
This method returns an instance of ListeningExecutorService. It is a heavier implementation of Executor that has many useful methods. It is similar to the deprecated sameThreadExecutor() method from previous versions of Guava.
This ExecutorService will run tasks on the thread that calls the execute() method.
ListeningExecutorService executor = MoreExecutors.newDirectExecutorService();
executor.execute(logThreadRun);
This executor has many useful methods such as invokeAll, invokeAny, awaitTermination, submit, isShutdown, isTerminated, shutdown, shutdownNow.
7. Conclusion
Guava 18 introduced several additions and improvements to its growing library of useful features. It is well-worth considering for use in your next project. The code samples in this article are available in the GitHub repository.