September 19, 2012

Google Guava Goodness: Joining Collection Elements

Sometimes I work on Java projects and then I miss the expressiveness of the Groovy language. For example to join elements in a list with a given separator to get a String value we only have to use the following code:

final List<String> names = ['mrhaki', 'JDriven', 'Groovy Rocks!']

assert names.join(',') == 'mrhaki,JDriven,Groovy Rocks!' 

When I cannot use Groovy then the Google Guava library has many methods and classes to make working with Java code easier. Let's see how we can use the Joiner class to join elements in a list with a separator:

package com.mrhaki.blog.guava;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.junit.Test;

import java.util.List;

// Create Joiner and use : as separator.
final Joiner joiner = Joiner.on(":");

// Sample list of three String elements.
final ImmutableList<String> names = ImmutableList.of("JDriven", "mrhaki", "Google Guava");

assert joiner.join(names).equals("JDriven:mrhaki:Google Guava");

// Create Joiner and make sure null values
// are not in the output.
final Joiner skipNulls = Joiner.on(",").skipNulls();

// Sample list of three String elements and a null value.
final List<String> namesWithNull = Lists.newArrayList("JDriven", null, "mrhaki", "Google Guava");

assert skipNulls.join(namesWithNull).equals("JDriven,mrhaki,Google Guava");

// Create Joiner and replace null values with
// String value <no value>.
final Joiner replaceNulls = Joiner.on(" + ").useForNull("<no value>");

final String expected = "JDriven + <no value> + mrhaki + Google Guava";
assert replaceNulls.join(names).equals(expected);

// MapJoiner is useful for joining key/value pairs
// in a map. The separator between each pair is
// defined with the on() method. The separator between
// the key and value is defined with the
// withKeyValueSeparator() method.
final Joiner.MapJoiner mapJoiner = Joiner.on('&').withKeyValueSeparator("=");

// Sample map with key/value pairs name/mrhaki and worksAt/JDriven.
final ImmutableMap<String, String> values = ImmutableMap.of("name", "mrhaki", "worksAt", "JDriven");

assert mapJoiner.join(values).equals("name=mrhaki&worksAt=JDriven");

(Sample written with Google Guava 13.0.1)

In Groovy code we could write the samples above like this:

final List<String> names = ['JDriven', 'mrhaki', 'Groovy rocks!']
assert names.join(':') == 'JDriven:mrhaki:Groovy rocks!'

final List<String> namesWithNull = ['JDriven', null, 'mrhaki', 'Groovy']
// findAll() will filter out the null values.
assert namesWithNull.findAll().join(',') == 'JDriven,mrhaki,Groovy'

final String expected = 'JDriven + <no value> + mrhaki + Groovy'
// Use collect to transform null value to 
// String value <no value>
assert namesWithNull.collect { it ?: '<no value>' }.join(' + ') == expected 

final Map<String, String> map = [name: 'mrhaki', worksAt: 'JDriven']
assert map.collect { k,v -> "$k=$v" }.join('&') == 'name=mrhaki&worksAt=JDriven'