1. Introduction

Groovy provides a substantial number of methods enhancing Java’s core capabilities.

In this tutorial, we’ll show how Groovy does this when checking for an element and finding it in several types of collections.

2. Test If Element Is Present

First, we’ll focus on just testing if a given collection contains an element.

2.1. List

Java itself provides several ways of checking for an item in a list with java.util.List:

  • The contains method
  • The indexOf method

As Groovy is a Java-compatible language, we can safely use them.

Let’s take a look at an example:

def "whenListContainsElement_thenCheckReturnsTrue"() {
    given:
    def list = ['a', 'b', 'c']

    expect:
    list.indexOf('a') > -1
    list.contains('a')
}

Apart from that, Groovy introduces the membership operator:

element in list

It’s one of many syntactic sugar operators provided by Groovy. With its help, we can simplify our code:

def "whenListContainsElement_thenCheckWithMembershipOperatorReturnsTrue"() {
    given:
    def list = ['a', 'b', 'c']

    expect:
    'a' in list
}

2.2. Set

As with the previous example, we can use the java.util.Set#contains method and the in operator:

def "whenSetContainsElement_thenCheckReturnsTrue"() {
    given:
    def set = ['a', 'b', 'c'] as Set

    expect:
    set.contains('a')
    'a' in set
}

2.3. Map

In the case of a Map, we can check for either the key or value directly:

def "whenMapContainsKeyElement_thenCheckReturnsTrue"() {
    given:
    def map = [a: 'd', b: 'e', c: 'f']

    expect:
    map.containsKey('a')
    !map.containsKey('e')
    map.containsValue('e')
}

Or use membership operator to find the matching key:

def "whenMapContainsKeyElement_thenCheckByMembershipReturnsTrue"() {
    given:
    def map = [a: 'd', b: 'e', c: 'f']

    expect:
    'a' in map
    'f' !in map
}

When used with maps, we should use the membership operator with care because this operator is a bit confusing to use with boolean values. Rather than testing for the presence of the key, the underlying mechanism retrieves the corresponding value from the map and just casts it to boolean:

def "whenMapContainsFalseBooleanValues_thenCheckReturnsFalse"() {
    given:
    def map = [a: true, b: false, c: null]

    expect:
    map.containsKey('b')
    'a' in map
    'b' !in map // get value of key 'b' and does the assertion
    'c' !in map
}

As we might see in the above example, it’s also a bit hazardous to use with null values either for the same reason. Groovy casts both false and null to boolean false.

3. All Match and Any Match

In most cases, we deal with collections composed of more complex objects. In this section, we’ll show how to check if the given collection contains at least one matching element or if all elements match a given predicate.

Let’s start by defining a simple class that we’ll use throughout our examples:

class Person {
    private String firstname
    private String lastname
    private Integer age

    // constructor, getters and setters
}

3.1. List/Set

This time, we’ll use a simple list of Person objects:

private final personList = [
  new Person("Regina", "Fitzpatrick", 25),
  new Person("Abagail", "Ballard", 26),
  new Person("Lucian", "Walter", 30),
]

As we mentioned before, Groovy is a Java-compatible language, so let’s first create an example using the Stream API introduced by Java 8:

def "givenListOfPerson_whenUsingStreamMatching_thenShouldEvaluateList"() {
    expect:
    personList.stream().anyMatch { it.age > 20 }
    !personList.stream().allMatch { it.age < 30 }
}

We can also use the Groovy methods DefaultGroovyMethods#any and DefaultGroovyMethods#every that perform the check directly on the collection:

def "givenListOfPerson_whenUsingCollectionMatching_thenShouldEvaluateList"() {
    expect:
    personList.any { it.age > 20 }
    !personList.every { it.age < 30 }
}

3.2. Map

Let’s start by defining a Map of Person objects mapped by Person#firstname:

private final personMap = [
  Regina : new Person("Regina", "Fitzpatrick", 25),
  Abagail: new Person("Abagail", "Ballard", 26),
  Lucian : new Person("Lucian", "Walter", 30)
]

We can evaluate it by either its keys, values, or by whole entries. Again, let’s first use the Stream API:

def "givenMapOfPerson_whenUsingStreamMatching_thenShouldEvaluateMap"() {
    expect:
    personMap.keySet().stream()
             .anyMatch { it == "Regina" }
    !personMap.keySet().stream()
              .allMatch { it == "Albert" }
    !personMap.values().stream()
              .allMatch { it.age < 30 }
    personMap.entrySet().stream()
             .anyMatch { it.key == "Abagail" && it.value.lastname == "Ballard" }
}

And then, the Groovy Collection API:

def "givenMapOfPerson_whenUsingCollectionMatching_thenShouldEvaluateMap"() {
    expect:
    personMap.keySet().any { it == "Regina" }
    !personMap.keySet().every { it == "Albert" }
    !personMap.values().every { it.age < 30 }
    personMap.any { firstname, person -> firstname == "Abagail" && person.lastname == "Ballard" }
}

As we can see, Groovy not only adequately replaces the Stream API when manipulating maps but also allows us to perform a check directly on the Map object instead of using the java.util.Map#entrySet method.

4. Find One or More Elements in a Collection

4.1. List/Set

We can also extract elements using predicates. Let’s start with the familiar Stream API approach:

def "givenListOfPerson_whenUsingStreamFind_thenShouldReturnMatchingElements"() {
    expect:
    personList.stream().filter { it.age > 20 }.findAny().isPresent()
    !personList.stream().filter { it.age > 30 }.findAny().isPresent()
    personList.stream().filter { it.age > 20 }.findAll().size() == 3
    personList.stream().filter { it.age > 30 }.findAll().isEmpty()
}

As we can see, the above example uses java.util.Optional for finding a single element as the Stream API forces that approach.

On the other hand, Groovy offers a much more compact syntax:

def "givenListOfPerson_whenUsingCollectionFind_thenShouldReturnMatchingElements"() {
    expect:
    personList.find { it.age > 20 } == new Person("Regina", "Fitzpatrick", 25)
    personList.find { it.age > 30 } == null
    personList.findAll { it.age > 20 }.size() == 3
    personList.findAll { it.age > 30 }.isEmpty()
}

By using Groovy’s API, we can skip creating a Stream and filtering it.

4.2. Map

In the case of a Map, there are several options to choose from. We can find elements amongst keys, values or complete entries. As the first two are basically a List or a Set, in this section we’ll only show an example of finding entries.

Let’s reuse our personMap from earlier:

def "givenMapOfPerson_whenUsingStreamFind_thenShouldReturnElements"() {
    expect:
    personMap.entrySet().stream()
             .filter { it.key == "Abagail" && it.value.lastname == "Ballard" }
             .findAny()
             .isPresent()

    personMap.entrySet().stream()
             .filter { it.value.age > 20 }
             .findAll()
             .size() == 3
}

And again, the simplified Groovy solution:

def "givenMapOfPerson_whenUsingCollectionFind_thenShouldReturnElements"() {
    expect:
    personMap.find { it.key == "Abagail" && it.value.lastname == "Ballard" }
    personMap.findAll { it.value.age > 20 }.size() == 3
}

In this case, the benefits are even more significant. We skip the java.util.Map#entrySet method and use a closure with a function provided on the Map.

5. Conclusion

In this article, we presented how Groovy simplifies checking for elements and finding them in several types of collections.

As always, the complete code examples used in this tutorial are available over on GitHub.