I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE COURSE

1. Overview

In this tutorial, we’ll illustrate how to filter and transform collections with Guava.

We will filter using Predicates, transform using the Functions that the library provides and finally, we’ll see how to combine both filtering and transforming.

Further reading:

New Stream, Comparator and Collector in Guava 21

Quick and practical guide to tools in the common.collect package in Guava 21.

Read more

Guide to Guava Multimap

A short guide to Guava Multimap in comparison with standard java.util.Map

Read more

Guide to Guava RangeSet

Learn how to use the Google Guava RangeSet and its implementations through practical examples.

Read more

2. Filter a Collection

Let’s start with a simple example of filtering a collection. We’ll be using an out of the box Predicate provided by the library and constructed via the Predicates utility class:

@Test
public void whenFilterWithIterables_thenFiltered() {
    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Iterable<String> result 
      = Iterables.filter(names, Predicates.containsPattern("a"));

    assertThat(result, containsInAnyOrder("Jane", "Adam"));
}

As you can see, we’re filtering the List of names to get only the names which contain the character “a” – and we’re using Iterables.filter() to do it.

Alternatively, we can make good use of Collections2.filter() API as well:

@Test
public void whenFilterWithCollections2_thenFiltered() {
    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Collection<String> result 
      = Collections2.filter(names, Predicates.containsPattern("a"));
    
    assertEquals(2, result.size());
    assertThat(result, containsInAnyOrder("Jane", "Adam"));

    result.add("anna");
    assertEquals(5, names.size());
}

A few things to note here – first, the output of Collections.filter() is a live view of the original collection – changes to one will be reflected in the other.

It’s also important to understand that now, the result is constrained by the predicate – if we add an element that doesn’t satisfy that Predicate, an IllegalArgumentException will be thrown:

@Test(expected = IllegalArgumentException.class)
public void givenFilteredCollection_whenAddingInvalidElement_thenException() {
    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Collection<String> result 
      = Collections2.filter(names, Predicates.containsPattern("a"));

    result.add("elvis");
}

3. Write Custom Filter Predicate

Next – let’s write our own Predicate instead of using one provided by the library. In the following example – we’ll define a predicate that only gets the names that start with “A” or “J”:

@Test
public void whenFilterCollectionWithCustomPredicate_thenFiltered() {
    Predicate<String> predicate = new Predicate<String>() {
        @Override
        public boolean apply(String input) {
            return input.startsWith("A") || input.startsWith("J");
        }
    };

    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Collection<String> result = Collections2.filter(names, predicate);

    assertEquals(3, result.size());
    assertThat(result, containsInAnyOrder("John", "Jane", "Adam"));
}

4. Combine multiple Predicates

We can combine multiple predicates using Predicates.or() and Predicates.and().
In the following example – we filter a List of names to get the names that start with “J” or don’t contain “a”:

@Test
public void whenFilterUsingMultiplePredicates_thenFiltered() {
    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Collection<String> result = Collections2.filter(names, 
      Predicates.or(Predicates.containsPattern("J"), 
      Predicates.not(Predicates.containsPattern("a"))));

    assertEquals(3, result.size());
    assertThat(result, containsInAnyOrder("John", "Jane", "Tom"));
}

5. Remove null values while Filtering a Collection

We can clean up the null values from a collection by filtering it with Predicates.notNull() as in the following example:

@Test
public void whenRemoveNullFromCollection_thenRemoved() {
    List<String> names = 
      Lists.newArrayList("John", null, "Jane", null, "Adam", "Tom");
    Collection<String> result = 
      Collections2.filter(names, Predicates.notNull());

    assertEquals(4, result.size());
    assertThat(result, containsInAnyOrder("John", "Jane", "Adam", "Tom"));
}

6. Check If All Elements in a Collection Match a Condition

Next, let’s check if all elements in a Collection match a certain condition. We’ll use Iterables.all() to check if all names contain “n” or “m”, then we’ll check if all elements contain “a”:

@Test
public void whenCheckingIfAllElementsMatchACondition_thenCorrect() {
    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");

    boolean result = Iterables.all(names, Predicates.containsPattern("n|m"));
    assertTrue(result);

    result = Iterables.all(names, Predicates.containsPattern("a"));
    assertFalse(result);
}

7. Transform a Collection

Now – let’s see how to transform a collection using a Guava Function. In the following example – we transform a List of names to a List of Integers (length of the name) with Iterables.transform():

@Test
public void whenTransformWithIterables_thenTransformed() {
    Function<String, Integer> function = new Function<String, Integer>() {
        @Override
        public Integer apply(String input) {
            return input.length();
        }
    };

    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Iterable<Integer> result = Iterables.transform(names, function);

    assertThat(result, contains(4, 4, 4, 3));
}

We can also use the Collections2.transform() API as in the following example:

@Test
public void whenTransformWithCollections2_thenTransformed() {
    Function<String,Integer> func = new Function<String,Integer>(){
        @Override
        public Integer apply(String input) {
            return input.length();
        }
    };

    List<String> names = 
      Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Collection<Integer> result = Collections2.transform(names, func);

    assertEquals(4, result.size());
    assertThat(result, contains(4, 4, 4, 3));

    result.remove(3);
    assertEquals(3, names.size());
}

Note that the output of Collections.transform() is a live view of the original Collection – changes to one affect the other.

And – same as before – if we try to add an element to the output Collection, an UnsupportedOperationException will be thrown.

8. Create Function from Predicate

We can also create Function from a Predicate using Functions.fromPredicate(). This is, of course, going to be a function that transforms the inputs to Boolean, according to the condition of the predicate.

In the following example, we transform a List of names to a list of booleans where each element represents if the name contains “m”:

@Test
public void whenCreatingAFunctionFromAPredicate_thenCorrect() {
    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Collection<Boolean> result =
      Collections2.transform(names,
      Functions.forPredicate(Predicates.containsPattern("m")));

    assertEquals(4, result.size());
    assertThat(result, contains(false, false, true, true));
}

9. Composition of two Functions

Next – let’s take a look at how to transform a Collection using a composed Function.

Functions.compose() returns the composition of two functions as it applies the second Function on the output of the first Function.

In the following example – the first Function transform the name into its length, then the second Function transforms the length to a boolean value which represents if the name’s length is even:

@Test
public void whenTransformingUsingComposedFunction_thenTransformed() {
    Function<String,Integer> f1 = new Function<String,Integer>(){
        @Override
        public Integer apply(String input) {
            return input.length();
        }
    };

    Function<Integer,Boolean> f2 = new Function<Integer,Boolean>(){
        @Override
        public Boolean apply(Integer input) {
            return input % 2 == 0;
        }
    };

    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Collection<Boolean> result = 
      Collections2.transform(names, Functions.compose(f2, f1));

    assertEquals(4, result.size());
    assertThat(result, contains(true, true, true, false));
}

10. Combine Filtering and Transforming

And now – let’s see another cool API that Guava has – one that will actually allow us to chain filtering and transforming together – the FluentIterable.

In the following example – we filter the List of names then transform it using FluentIterable:

@Test
public void whenFilteringAndTransformingCollection_thenCorrect() {
    Predicate<String> predicate = new Predicate<String>() {
        @Override
        public boolean apply(String input) {
            return input.startsWith("A") || input.startsWith("T");
        }
    };

    Function<String, Integer> func = new Function<String,Integer>(){
        @Override
        public Integer apply(String input) {
            return input.length();
        }
    };

    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Collection<Integer> result = FluentIterable.from(names)
                                               .filter(predicate)
                                               .transform(func)
                                               .toList();

    assertEquals(2, result.size());
    assertThat(result, containsInAnyOrder(4, 3));
}

It is worth mentioning that, in some cases, the imperative version is more readable and should be preferred to the functional approach.

11. Conclusion

Finally, we learned how to filter and transform collections using Guava. We used the Collections2.filter() and Iterables.filter() APIs for filtering, as well as Collections2.transform() and Iterables.transform() to transform collections.

Finally, we took a quick look at the very interesting FluentIterable fluent API to combine both filtering and transforming.

The implementation of all these examples and code snippets can be found in the GitHub project – this is a Maven-based project, so it should be easy to import and run as it is.

I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE LESSONS

newest oldest most voted
Notify of
padjiman
Guest
padjiman

very cool article. for item 10 , you should mention that the non-guava way of doing it is much more recommended (check e.g. what is written at the beginning of the official doc here: https://code.google.com/p/guava-libraries/wiki/FunctionalExplained ). i understand it was mainly for the sake of the example, but i guess it worth mentioning it is a very bad practice for that particular example to do it that way. tks in any case for this very clear tutorial

Eugen Paraschiv
Guest

That’s a good point – in many cases, it’s a matter of balancing out readability with conciseness. The example I went over in the section is is, as you point out – just an example of HOW to do it, not advice to always do it – I added a small clarification in that section to make that fact clear. Thanks again,
Eugen.

Sourabh Sharma
Guest
Sourabh Sharma

This is a nice article

Eugen Paraschiv
Guest

Glad you found it useful Gourabh. Cheers,
Eugen.

Angelo
Guest
Angelo

Eugen never disappoint. You should pop in Bristol for some talks that would it be great.

Eugen Paraschiv
Guest

Glad you liked the article Angelo.
Sure, if I’m ever in Bristol I’ll definitely reach out and maybe plan a JUG talk. Cheers,
Eugen.

Joan
Guest
Joan

Clear, step by step approach makes it very easy to understand. I stumbled across FluentIterable in some code I was debugging, had no idea what it could mean. Now I get it and really it’s no big deal.

Eugen Paraschiv
Guest

Yeah, the FluentIterable can be quite handy, and can reduce a lot of verbosity 🙂
Cheers,
Eugen.