Encontrar elementos en colecciones en Groovy

1. Introducción

Groovy proporciona una cantidad sustancial de métodos que mejoran las capacidades centrales de Java.

En este tutorial, mostraremos cómo Groovy hace esto cuando busca un elemento y lo encuentra en varios tipos de colecciones.

2. Pruebe si el elemento está presente

Primero, nos centraremos en probar si una colección determinada contiene un elemento.

2.1. Lista

Java en sí proporciona varias formas de verificar un elemento en una lista con java.util.List :

  • El método contiene
  • El método indexOf

Como Groovy es un lenguaje compatible con Java, podemos utilizarlos de forma segura.

Echemos un vistazo a un ejemplo:

@Test void whenListContainsElement_thenCheckReturnsTrue() { def list = ['a', 'b', 'c'] assertTrue(list.indexOf('a') > -1) assertTrue(list.contains('a')) }

Aparte de eso, Groovy presenta el operador de membresía:

element in list

Es uno de los muchos operadores sintácticos de azúcar proporcionados por Groovy. Con su ayuda, podemos simplificar nuestro código:

@Test void whenListContainsElement_thenCheckWithMembershipOperatorReturnsTrue() { def list = ['a', 'b', 'c'] assertTrue('a' in list) }

2.2. Conjunto

Como en el ejemplo anterior, podemos usar el método java.util.Set # contains y el operador in :

@Test void whenSetContainsElement_thenCheckReturnsTrue() { def set = ['a', 'b', 'c'] as Set assertTrue(set.contains('a')) assertTrue('a' in set) }

2.3. Mapa

En el caso de un mapa , podemos verificar la clave o el valor directamente:

@Test void whenMapContainsKeyElement_thenCheckReturnsTrue() { def map = [a: 'd', b: 'e', c: 'f'] assertTrue(map.containsKey('a')) assertFalse(map.containsKey('e')) assertTrue(map.containsValue('e')) }

O use el operador de membresía para encontrar la clave correspondiente:

@Test void whenMapContainsKeyElement_thenCheckByMembershipReturnsTrue() { def map = [a: 'd', b: 'e', c: 'f'] assertTrue('a' in map) assertFalse('f' in map) }

Cuando se usa con mapas, debemos usar el operador de membresía con cuidado porque este operador es un poco confuso de usar con valores booleanos. En lugar de probar la presencia de la clave, el mecanismo subyacente recupera el valor correspondiente del mapa y simplemente lo convierte en booleano:

@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) }

Como podemos ver en el ejemplo anterior, también es un poco peligroso usarlo con valores nulos por la misma razón. Groovy convierte tanto falso como nulo a booleano falso .

3. Todos los partidos y todos los partidos

En la mayoría de los casos, tratamos con colecciones compuestas por objetos más complejos. En esta sección, mostraremos cómo verificar si la colección dada contiene al menos un elemento coincidente o si todos los elementos coinciden con un predicado dado.

Comencemos por definir una clase simple que usaremos a lo largo de nuestros ejemplos:

class Person { private String firstname private String lastname private Integer age // constructor, getters and setters }

3.1. Lista / Conjunto

Esta vez, usaremos una lista simple de objetos Person :

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  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.

Como siempre, los ejemplos de código completos utilizados en este tutorial están disponibles en GitHub.