Localizando elementos em coleções no Groovy

Localizando elementos em coleções no Groovy

1. Introdução

Groovy fornece um número substancial de métodos que aprimoram os recursos principais do Java.

Neste tutorial, mostraremos como o Groovy faz isso quandochecking for an element and finding it in several types of collections.

2. Teste se o elemento está presente

Primeiro, vamos nos concentrar apenas em testar se uma determinada coleção contém um elemento.

2.1. List

O próprio Java fornece várias maneiras dechecking for an item in a list with java.util.List:

  • O métodocontains

  • O métodoindexOf 

Como o Groovy é uma linguagem compatível com Java, podemos usá-los com segurança.

Vamos dar uma olhada em um exemplo:

@Test
void whenListContainsElement_thenCheckReturnsTrue() {
    def list = ['a', 'b', 'c']

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

Além disso,Groovy introduces the membership operator:

element in list

É um dos muitos operadores de açúcar sintático fornecidos pela Groovy. Com sua ajuda, podemos simplificar nosso código:

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

    assertTrue('a' in list)
}

2.2. Set

Como no exemplo anterior, podemos usar o métodojava.util.Set#containse o operadorin:

@Test
void whenSetContainsElement_thenCheckReturnsTrue() {
    def set = ['a', 'b', 'c'] as Set

    assertTrue(set.contains('a'))
    assertTrue('a' in set)
}

2.3. Map

No caso de umMap, podemos verificar a chave ou o valor diretamente:

@Test
void whenMapContainsKeyElement_thenCheckReturnsTrue() {
    def map = [a: 'd', b: 'e', c: 'f']

    assertTrue(map.containsKey('a'))
    assertFalse(map.containsKey('e'))
    assertTrue(map.containsValue('e'))
}

Ou use o operador de associação para encontrar a chave correspondente:

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

    assertTrue('a' in map)
    assertFalse('f' in map)
}

Quando usado com mapas, devemos usar o operador de associação com cuidado porquethis operator is a bit confusing to use with boolean values. Em vez de testar a presença da chave, o mecanismo subjacente recupera o valor correspondente do mapa e apenascasts 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)
}

Como podemos ver no exemplo acima, também éa bit hazardous to use with null values pelo mesmo motivo. Groovy convertefalse enull emfalse booleano.

3. Todas as correspondências e qualquer correspondência

Na maioria dos casos, lidamos com coleções compostas por objetos mais complexos. Nesta seção, mostraremos como verificar se a coleção fornecida contém pelo menos um elemento correspondente ou se todos os elementos correspondem a um determinado predicado.

Vamos começar definindo uma classe simples que usaremos em nossos exemplos:

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

    // constructor, getters and setters
}

3.1. List/Set

Desta vez, usaremos uma lista simples de objetosPerson:

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

Como mencionamos antes,Groovy is a Java-compatible language, então vamos primeiro criar um exemplo usandoStream API introduzido pelo Java 8:

@Test
void givenListOfPerson_whenUsingStreamMatching_thenShouldEvaluateList() {
    assertTrue(personList.stream().anyMatch {it.age > 20})
    assertFalse(personList.stream().allMatch {it.age < 30})
}

Também podemos usar os métodos GroovyDefaultGroovyMethods#any andDefaultGroovyMethods#every que realizam a verificação diretamente na coleção:

@Test
void givenListOfPerson_whenUsingCollectionMatching_thenShouldEvaluateList() {
    assertTrue(personList.any {it.age > 20})
    assertFalse(personList.every {it.age < 30})
}

3.2. Map

Vamos começar definindo umMap dePerson objetos mapeados porPerson#firstname:

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

Podemos avaliá-lo por suas chaves, valores ou por entradas inteiras. Novamente, vamos primeiro usar a APIStream:

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

E então, a API de coleção Groovy:

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

Como podemos ver, o Groovy não apenas substitui adequadamente a APIStream ao manipular mapas, mas também nos permite realizar uma verificação diretamente no objetoMap em vez de usar o métodojava.util.Map#entrySet.

4. Encontre um ou mais elementos em uma coleção

4.1. List/Set

Também podemos extrair elementos usando predicados. Vamos começar com a familiar abordagem de APIStream:

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

Como podemos ver, o exemplo acima usajava.util.Optional para encontrar um único elemento, pois a APIStream força essa abordagem.

Por outro lado, o Groovy oferece uma sintaxe muito mais compacta:

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

Usando a API do Groovy,we can skip creating a Stream and filtering it.

4.2. Map

No caso de umMap, t, existem várias opções para escolher. We can find elements amongst keys, values or complete entries. Como os dois primeiros são basicamente aList ouSet, nesta seção mostraremos apenas um exemplo de como encontrar entradas.

Vamos reutilizar nossopersonMap anterior:

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

E, novamente, a solução simplificada do Groovy:

@Test
void givenMapOfPerson_whenUsingCollectionFind_thenShouldReturnElements() {
    assertNotNull(personMap.find {it.key == "Abagail" && it.value.lastname == "Ballard"})
    assertTrue(personMap.findAll {it.value.age > 20}.size() == 3)
}

Nesse caso, os benefícios são ainda mais significativos. Ignoramos o métodojava.util.Map#entrySet e usamos um encerramento com uma função fornecida emMap.

5. Conclusão

Neste artigo, apresentamos comoGroovy simplifies checking for elements and finding them in several types of collections.

Como sempre, os exemplos de código completos usados ​​neste tutorial estão disponíveisover on GitHub.