Перейти к основному содержимому

Поиск элементов в коллекциях в Groovy

· 5 мин. чтения

Задача: Сумма двух чисел

Напишите функцию twoSum. Которая получает массив целых чисел nums и целую сумму target, а возвращает индексы двух чисел, сумма которых равна target. Любой набор входных данных имеет ровно одно решение, и вы не можете использовать один и тот же элемент дважды. Ответ можно возвращать в любом порядке...

ANDROMEDA

1. Введение

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

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

2. Проверьте, присутствует ли элемент

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

2.1. Список

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

  • Метод содержит _
  • Метод 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. Установлен

Как и в предыдущем примере, мы можем использовать метод 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. карта

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

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

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

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

Как мы могли видеть в приведенном выше примере, также немного опасно использовать значения null по той же причине. Groovy приводит как false , так и null к логическому значению false .

3. Все совпадения и любые совпадения

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

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

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

// constructor, getters and setters
}

3.1. Список/Набор

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

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

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

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

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

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

3.2. карта

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

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

Мы можем оценить его либо по его ключам, значениям, либо по целым записям. Опять же, давайте сначала воспользуемся 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"})
}

А затем API коллекции 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"})
}

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

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

4.1. Список/Набор

Мы также можем извлекать элементы с помощью предикатов. Начнем со знакомого подхода 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())
}

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

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

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

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

4.2. карта

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

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

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

И снова упрощенное решение 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)
}

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

5. Вывод

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

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