1. 简介

Groovy 是一门与 Java 兼容的语言,它在 Java 的基础上提供了大量增强功能。

本文将重点介绍 Groovy 在 检查元素是否存在以及在多种集合类型中查找元素时的便利方法

2. 判断元素是否存在

我们先来看如何判断某个集合中是否包含特定元素。

2.1. List

Java 中的 java.util.List 提供了以下几种方式来判断元素是否存在:

contains 方法
indexOf 方法

Groovy 完全兼容 Java,因此这些方法可以直接使用:

def "whenListContainsElement_thenCheckReturnsTrue"() {
    given:
    def list = ['a', 'b', 'c']

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

除此之外,Groovy 还引入了一个语法糖 —— 成员运算符(membership operator)

element in list

这个操作符让代码更加简洁直观:

def "whenListContainsElement_thenCheckWithMembershipOperatorReturnsTrue"() {
    given:
    def list = ['a', 'b', 'c']

    expect:
    'a' in list
}

2.2. Set

Set 的判断方式和 List 类似,同样支持 contains 和成员运算符:

def "whenSetContainsElement_thenCheckReturnsTrue"() {
    given:
    def set = ['a', 'b', 'c'] as Set

    expect:
    set.contains('a')
    'a' in set
}

2.3. Map

Map 中可以分别检查 key 和 value 是否存在:

def "whenMapContainsKeyElement_thenCheckReturnsTrue"() {
    given:
    def map = [a: 'd', b: 'e', c: 'f']

    expect:
    map.containsKey('a')
    !map.containsKey('e')
    map.containsValue('e')
}

也可以使用成员运算符来判断 key 是否存在:

def "whenMapContainsKeyElement_thenCheckByMembershipReturnsTrue"() {
    given:
    def map = [a: 'd', b: 'e', c: 'f']

    expect:
    'a' in map
    'f' !in map
}

⚠️ 注意: 成员运算符在 Map 中的使用要格外小心。它不会直接判断 key 是否存在,而是取出 key 对应的 value 并将其转换为 boolean 值进行判断。

def "whenMapContainsFalseBooleanValues_thenCheckReturnsFalse"() {
    given:
    def map = [a: true, b: false, c: null]

    expect:
    map.containsKey('b')
    'a' in map
    'b' !in map // 实际上是获取 value 后转换为 boolean
    'c' !in map
}

如上所示,当 value 为 falsenull 时都会被判定为 false,这在某些场景下可能会导致误判。

3. 全部匹配与任意匹配

在实际开发中,我们经常需要判断集合中的元素是否满足某种条件。Groovy 提供了简洁的方法来处理这类需求。

首先,我们定义一个用于示例的类:

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

    // constructor, getters and setters
}

3.1. List/Set

使用 Person 对象组成的 List 作为示例:

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

Java 8 的 Stream API 可以这样使用:

def "givenListOfPerson_whenUsingStreamMatching_thenShouldEvaluateList"() {
    expect:
    personList.stream().anyMatch { it.age > 20 }
    !personList.stream().allMatch { it.age < 30 }
}

Groovy 提供了更简洁的写法:

def "givenListOfPerson_whenUsingCollectionMatching_thenShouldEvaluateList"() {
    expect:
    personList.any { it.age > 20 }
    !personList.every { it.age < 30 }
}

3.2. Map

定义一个以 firstname 为 key 的 Map:

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

使用 Stream API 的方式:

def "givenMapOfPerson_whenUsingStreamMatching_thenShouldEvaluateMap"() {
    expect:
    personMap.keySet().stream()
             .anyMatch { it == "Regina" }
    !personMap.keySet().stream()
              .allMatch { it == "Albert" }
    !personMap.values().stream()
              .allMatch { it.age < 30 }
    personMap.entrySet().stream()
             .anyMatch { it.key == "Abagail" && it.value.lastname == "Ballard" }
}

Groovy 的方式更加直观:

def "givenMapOfPerson_whenUsingCollectionMatching_thenShouldEvaluateMap"() {
    expect:
    personMap.keySet().any { it == "Regina" }
    !personMap.keySet().every { it == "Albert" }
    !personMap.values().every { it.age < 30 }
    personMap.any { firstname, person -> firstname == "Abagail" && person.lastname == "Ballard" }
}

Groovy 不仅简化了 Stream API 的使用,还允许我们直接在 Map 上进行判断,无需调用 entrySet()

4. 查找一个或多个元素

4.1. List/Set

使用 Stream API 查找元素:

def "givenListOfPerson_whenUsingStreamFind_thenShouldReturnMatchingElements"() {
    expect:
    personList.stream().filter { it.age > 20 }.findAny().isPresent()
    !personList.stream().filter { it.age > 30 }.findAny().isPresent()
    personList.stream().filter { it.age > 20 }.findAll().size() == 3
    personList.stream().filter { it.age > 30 }.findAll().isEmpty()
}

Groovy 的写法更加简洁:

def "givenListOfPerson_whenUsingCollectionFind_thenShouldReturnMatchingElements"() {
    expect:
    personList.find { it.age > 20 } == new Person("Regina", "Fitzpatrick", 25)
    personList.find { it.age > 30 } == null
    personList.findAll { it.age > 20 }.size() == 3
    personList.findAll { it.age > 30 }.isEmpty()
}

优势: 无需手动创建 Stream,直接使用集合方法即可。

4.2. Map

Map 中可以查找 key、value 或 entry。这里我们只展示 entry 的查找方式。

使用 Stream API:

def "givenMapOfPerson_whenUsingStreamFind_thenShouldReturnElements"() {
    expect:
    personMap.entrySet().stream()
             .filter { it.key == "Abagail" && it.value.lastname == "Ballard" }
             .findAny()
             .isPresent()

    personMap.entrySet().stream()
             .filter { it.value.age > 20 }
             .findAll()
             .size() == 3
}

Groovy 的方式:

def "givenMapOfPerson_whenUsingCollectionFind_thenShouldReturnElements"() {
    expect:
    personMap.find { it.key == "Abagail" && it.value.lastname == "Ballard" }
    personMap.findAll { it.value.age > 20 }.size() == 3
}

优势显著: 无需调用 entrySet(),直接在 Map 上进行操作,语法更加自然。

5. 总结

本文介绍了 Groovy 如何简化集合中元素的查找和判断操作,包括:

  • 使用成员运算符简化判断逻辑
  • 使用 anyevery 判断集合是否满足条件
  • 使用 findfindAll 快速查找元素

这些方法不仅代码更简洁,而且语义更清晰,是 Groovy 开发中常用的技巧。

完整示例代码可从 GitHub 获取。


原始标题:Finding Elements in Collections in Groovy