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:
@Test void whenListContainsElement_thenCheckReturnsTrue() { def list = ['a', 'b', 'c'] assertTrue(list.indexOf('a') > -1) assertTrue(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:
@Test void whenListContainsElement_thenCheckWithMembershipOperatorReturnsTrue() { def list = ['a', 'b', 'c'] assertTrue('a' in list) }
2.2. Set
As with the previous example, we can use the java.util.Set#contains method and the in operator:
@Test void whenSetContainsElement_thenCheckReturnsTrue() { def set = ['a', 'b', 'c'] as Set assertTrue(set.contains('a')) assertTrue('a' in set) }
2.3. Map
In the case of a Map, we can check for either the key or value directly:
@Test void whenMapContainsKeyElement_thenCheckReturnsTrue() { def map = [a: 'd', b: 'e', c: 'f'] assertTrue(map.containsKey('a')) assertFalse(map.containsKey('e')) assertTrue(map.containsValue('e')) }
Or use membership operator to find the matching key:
@Test void whenMapContainsKeyElement_thenCheckByMembershipReturnsTrue() { def map = [a: 'd', b: 'e', c: 'f'] assertTrue('a' in map) assertFalse('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:
@Test void whenMapContainsFalseBooleanValues_thenCheckReturnsFalse() { def map = [a: true, b: false, c: null] assertTrue(map.containsKey('b')) assertTrue('a' in map) assertFalse('b' in map) assertFalse('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:
@Test void givenListOfPerson_whenUsingStreamMatching_thenShouldEvaluateList() { assertTrue(personList.stream().anyMatch {it.age > 20}) assertFalse(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:
@Test void givenListOfPerson_whenUsingCollectionMatching_thenShouldEvaluateList() { assertTrue(personList.any {it.age > 20}) assertFalse(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:
@Test void givenMapOfPerson_whenUsingStreamMatching_thenShouldEvaluateMap() { assertTrue(personMap.keySet().stream().anyMatch {it == "Regina"}) assertFalse(personMap.keySet().stream().allMatch {it == "Albert"}) assertFalse(personMap.values().stream().allMatch {it.age < 30}) assertTrue(personMap.entrySet().stream().anyMatch {it.key == "Abagail" && it.value.lastname == "Ballard"}) }
And then, the Groovy Collection API:
@Test void givenMapOfPerson_whenUsingCollectionMatching_thenShouldEvaluateMap() { assertTrue(personMap.keySet().any {it == "Regina"}) assertFalse(personMap.keySet().every {it == "Albert"}) assertFalse(personMap.values().every {it.age < 30}) assertTrue(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:
@Test void givenListOfPerson_whenUsingStreamFind_thenShouldReturnMatchingElements() { assertTrue(personList.stream().filter {it.age > 20}.findAny().isPresent()) assertFalse(personList.stream().filter {it.age > 30}.findAny().isPresent()) assertTrue(personList.stream().filter {it.age > 20}.findAll().size() == 3) assertTrue(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:
@Test void givenListOfPerson_whenUsingCollectionFind_thenShouldReturnMatchingElements() { assertNotNull(personList.find {it.age > 20}) assertNull(personList.find {it.age > 30}) assertTrue(personList.findAll {it.age > 20}.size() == 3) assertTrue(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:
@Test void givenMapOfPerson_whenUsingStreamFind_thenShouldReturnElements() { assertTrue( personMap.entrySet().stream() .filter {it.key == "Abagail" && it.value.lastname == "Ballard"} .findAny().isPresent()) assertTrue( personMap.entrySet().stream() .filter {it.value.age > 20} .findAll().size() == 3) }
And again, the simplified Groovy solution:
@Test void givenMapOfPerson_whenUsingCollectionFind_thenShouldReturnElements() { assertNotNull(personMap.find {it.key == "Abagail" && it.value.lastname == "Ballard"}) assertTrue(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.