Filtering and Transforming Collections in Guava

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.

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.

Related posts:

Spring WebClient Filters
Java Program to Generate Random Numbers Using Probability Distribution Function
@DynamicUpdate with Spring Data JPA
Create a Custom Auto-Configuration with Spring Boot
Java Program to Implement Direct Addressing Tables
Java Program to Implement D-ary-Heap
Generating Random Dates in Java
Ignore Null Fields with Jackson
Guide to BufferedReader
Creating a Custom Starter with Spring Boot
Java Program to Find the Number of Ways to Write a Number as the Sum of Numbers Smaller than Itself
Java Program to Use the Bellman-Ford Algorithm to Find the Shortest Path
Java Program to Solve TSP Using Minimum Spanning Trees
Giới thiệu HATEOAS
Java Program to Perform Insertion in a 2 Dimension K-D Tree
Java Program to Check if a Matrix is Invertible
Java Program to Find ith Largest Number from a Given List Using Order-Statistic Algorithm
Java Program to Generate a Graph for a Given Fixed Degree Sequence
Java Program to Implement Johnson’s Algorithm
Java Program to Implement Traveling Salesman Problem using Nearest neighbour Algorithm
Display Auto-Configuration Report in Spring Boot
Truyền giá trị và tham chiếu trong java
Biến trong java
Simplify the DAO with Spring and Java Generics
Java Program to Generate All Pairs of Subsets Whose Union Make the Set
Java Program to Check whether Graph is Biconnected
Java Program to Perform Postorder Non-Recursive Traversal of a Given Binary Tree
Injecting Prototype Beans into a Singleton Instance in Spring
Java Program to Find Nearest Neighbor for Dynamic Data Set
Logout in an OAuth Secured Application
Java Program to Implement Hash Tables with Linear Probing
Guide to Character Encoding