«1. Введение

Groovy предоставляет значительное количество методов, расширяющих основные возможности Java.

В этом уроке мы покажем, как Groovy делает это при проверке элемента и поиске его в нескольких типах коллекций.

2. Проверка наличия элемента

Во-первых, мы сосредоточимся только на проверке наличия элемента в данной коллекции.

2.1. Список

Сама Java предоставляет несколько способов проверки элемента в списке с помощью java.util.List:

    Метод contains Метод indexOf

Так как Groovy является Java-совместимым языком, мы можем безопасно использовать их .

Давайте рассмотрим пример:

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

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

Кроме того, Groovy представляет оператор принадлежности:

element in list

Это один из многих синтаксических сахарных операторов, предоставляемых Groovy. С его помощью мы можем упростить наш код:

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

    assertTrue('a' in list)
}

2.2. Set

Как и в предыдущем примере, мы можем использовать метод java.util.Set#contains и оператор in:

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

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

2.3. Карта

В случае карты мы можем проверить либо ключ, либо значение напрямую:

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

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

Или использовать оператор принадлежности, чтобы найти соответствующий ключ:

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

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

При использовании с картами мы следует использовать оператор членства с осторожностью, потому что этот оператор немного сбивает с толку при использовании с логическими значениями. Вместо того, чтобы проверять наличие ключа, базовый механизм извлекает соответствующее значение из карты и просто приводит его к логическому значению: нулевые значения либо по той же причине. Groovy приводит как false, так и null к логическому значению false.

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

3. All Match и Any Match

В большинстве случаев мы имеем дело с коллекциями, состоящими из более сложных объектов. В этом разделе мы покажем, как проверить, содержит ли данная коллекция хотя бы один соответствующий элемент или все элементы соответствуют заданному предикату.

Начнем с определения простого класса, который мы будем использовать в наших примерах:

3.1. List/Set

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

    // constructor, getters and setters
}

На этот раз мы будем использовать простой список объектов Person:

Как мы упоминали ранее, Groovy — это язык, совместимый с Java, поэтому давайте сначала создадим пример, используя представленный Stream API. по Java 8:

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

Мы также можем использовать методы Groovy DefaultGroovyMethods#any и DefaultGroovyMethods#every, которые выполняют проверку непосредственно на коллекции:

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

3.2. Карта

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

Давайте начнем с определения карты объектов Person, сопоставленных Person#firstname:

Мы можем оценить ее либо по ее ключам, значениям, либо по целым записям. Опять же, давайте сначала воспользуемся Stream API:

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

А затем Groovy Collection 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"})
}

Как мы видим, Groovy не только адекватно заменяет Stream API при манипулировании картами, но также позволяет нам выполнить проверку непосредственно в объекте Map вместо использования метода java.util.Map#entrySet.

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

4. Найти один или несколько элементов в коллекции

4.1. List/Set

Мы также можем извлекать элементы, используя предикаты. Давайте начнем со знакомого подхода Stream API:

Как мы видим, в приведенном выше примере используется java.util.Optional для поиска одного элемента, поскольку Stream API вынуждает использовать этот подход.

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

С другой стороны, Groovy предлагает гораздо более компактный синтаксис:

Используя Groovy API, мы можем пропустить создание потока и его фильтрацию.

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

4.2. Карта

В случае карты есть несколько вариантов на выбор. Мы можем найти элементы среди ключей, значений или полных записей. Поскольку первые два в основном представляют собой список или набор, в этом разделе мы покажем только пример поиска записей.

Давайте повторно используем нашу карту personMap из предыдущего:

И снова упрощенное решение Groovy:

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

В этом случае преимущества еще значительнее. Мы пропускаем метод java.util.Map#entrySet и используем замыкание с функцией, предоставляемой на карте.

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

5. Заключение

В этой статье мы представили, как Groovy упрощает проверку элементов и их поиск в нескольких типах коллекций.

Как всегда, полные примеры кода, используемые в этом руководстве, доступны на GitHub.

«