文章目录

Groovy 集合操作:each、find、collect

发布于 2026-04-05 14:26:54 · 浏览 14 次 · 评论 0 条

Groovy 集合操作:each、find、collect

Groovy 作为一门运行在 JVM 上的动态语言,对集合操作提供了极其便捷的支持。eachfindcollect 是日常开发中最常用的三个方法,它们分别对应遍历、查找和转换三种核心操作。掌握这三个方法,能让你在处理数据时事半功倍。


1 准备工作:创建测试集合

在正式开始之前,先创建一个用于演示的集合。打开 Groovy Shell 或脚本,输入以下代码:

def numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def names = ["Alice", "Bob", "Charlie", "Diana"]

本文所有示例都将基于这两个集合展开。


2 each:遍历集合中的每一个元素

each 方法是 Groovy 中最基础的集合遍历方式。它接受一个闭包作为参数,闭包中的 it 代表当前遍历到的元素。使用 each 可以对集合中的每个元素执行相同的操作,而无需编写传统的 for 循环。

2.1 基本语法与使用

// 遍历数字集合,输出每个数字
numbers.each { println it }

// 遍历姓名集合,输出带前缀的姓名
names.each { println "姓名: ${it}" }
```

执行上述代码,第一段会依次打印 1 到 10,第二段会打印带有"姓名:"前缀的所有姓名。闭包中的 `it` 是 Groovy 的默认参数名,代表当前元素。如果你想要更语义化的变量名,可以显式声明参数:

```groovy
numbers.each { num -> println "当前数字: ${num}" }

2.2 遍历 Map 集合

each 方法同样适用于 Map 类型的数据结构:

def userMap = [name: "张三", age: 25, city: "北京"]

// 遍历 Map,key 和 value 都会传入闭包
userMap.each { key, value -> println "${key} = ${value}" }

对于 Map,each 接受两个参数的闭包,第一个参数是键,第二个参数是值。这种设计让遍历键值对变得非常自然。

2.3 配合索引使用

有时你需要知道当前遍历到第几个元素,这时候可以使用 eachWithIndex 方法:

names.eachWithIndex { name, index -> 
    println "索引 ${index}: ${name}" 
}

输出结果会显示每个元素的序号,从 0 开始计数。


3 find:快速定位符合条件的元素

find 方法用于从集合中查找第一个符合条件的元素并返回。如果没有任何元素符合条件,则返回 null。这个方法在需要从大量数据中筛选特定记录时非常实用。

3.1 基本用法

// 查找第一个大于 5 的数字
def result = numbers.find { it > 5 }
println result  // 输出: 6

// 查找第一个以 "A" 开头的姓名
def foundName = names.find { it.startsWith("A") }
println foundName  // 输出: Alice

find 方法会在找到第一个匹配项后立即停止遍历,不会继续检查后续元素。这种"短路"行为对于大集合来说能显著提升性能。

3.2 查找不存在的元素

// 查找大于 100 的数字
def notFound = numbers.find { it > 100 }
println notFound  // 输出: null

当没有找到匹配元素时,返回值是 null,你在使用前最好做空值判断:

def result = numbers.find { it > 100 }
if (result != null) {
    println "找到: ${result}"
} else {
    println "未找到符合条件的元素"
}
```

### 3.3 findAll:查找所有匹配元素

如果你需要获取所有符合条件的元素,而不是仅仅第一个,应该使用 `findAll` 方法:

```groovy
// 查找所有大于 5 的数字
def allMatches = numbers.findAll { it > 5 }
println allMatches  // 输出: [6, 7, 8, 9, 10]

// 查找所有以 "A" 或 "B" 开头的姓名
def filteredNames = names.findAll { it.startsWith("A") || it.startsWith("B") }
println filteredNames  // 输出: [Alice, Bob]
```

`findAll` 返回的是一个新集合,包含所有满足条件的元素。而 `find` 只返回第一个匹配项。

---

## 4 collect:元素转换与映射

`collect` 是 Groovy 中最强大的集合操作方法之一。它遍历集合中的每个元素,对每个元素执行转换操作,然后将所有转换结果收集起来组成一个新的集合。这个方法在数据清洗、格式转换等场景中使用频率极高。

### 4.1 基本语法

```groovy
// 将每个数字乘以 2
def doubled = numbers.collect { it * 2 }
println doubled  // 输出: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

// 将所有姓名转换为大写
def upperCaseNames = names.collect { it.toUpperCase() }
println upperCaseNames  // 输出: [ALICE, BOB, CHARLIE, DIANA]
```

`collect` 的核心价值在于它能够将一种类型的数据转换为另一种类型,实现集合的"映射"操作。

### 4.2 配合对象使用

假设你有一个用户列表,需要提取某个特定字段:

```groovy
class User {
    String name
    int age
}

def users = [
    new User(name: "张三", age: 25),
    new User(name: "李四", age: 30),
    new User(name: "王五", age: 28)
]

// 提取所有用户的姓名
def userNames = users.collect { it.name }
println userNames  // 输出: [张三, 李四, 王五]

// 计算所有用户的年龄总和
def totalAge = users.collect { it.age }.sum()
println totalAge  // 输出: 83
```

第二个示例展示了 `collect` 的组合用法:先通过 `collect` 提取年龄字段得到新集合,再调用 `sum()` 方法求和。

### 4.3 转换为不同类型

`collect` 不仅可以改变数值,还能完全改变元素类型:

```groovy
// 将数字转换为描述性字符串
def descriptions = numbers.collect { "数字: ${it}" }
println descriptions  
// 输出: [数字: 1, 数字: 2, ..., 数字: 10]

// 将数字集合转换为布尔值集合(判断是否大于5)
def bools = numbers.collect { it > 5 }
println bools  
// 输出: [false, false, false, false, false, true, true, true, true, true]

5 实战场景:组合使用三种方法

在实际开发中,eachfindcollect 很少单独使用,更多的是根据业务逻辑组合运用。以下是一个完整的示例场景。

5.1 场景描述

假设你有一个商品列表,每个商品是一个 Map,包含 name(名称)、price(价格)和 category(分类)三个字段。现在需要完成以下任务:

  1. 打印所有商品信息
  2. 找出价格最高的商品
  3. 计算所有商品的总价
  4. 将所有商品名称转换为大写
def products = [
    [name: "笔记本电脑", price: 5999, category: "电子产品"],
    [name: "机械键盘", price: 399, category: "电子产品"],
    [name: "办公椅", price: 899, category: "家具"],
    [name: "咖啡机", price: 1299, category: "电器"],
    [name: "显示器", price: 1899, category: "电子产品"]
]

// 任务1:打印所有商品信息
println "=== 商品列表 ==="
products.each { p -> 
    println "${p.name} - ¥${p.price} - ${p.category}" 
}

// 任务2:找出价格最高的商品
def expensiveProduct = products.find { p -> p.price == products.collect { it.price }.max() }
println "\n最贵商品: ${expensiveProduct.name} - ¥${expensiveProduct.price}"

// 任务3:计算所有商品的总价
def totalPrice = products.collect { it.price }.sum()
println "\n商品总价: ¥${totalPrice}"

// 任务4:将所有商品名称转换为大写
def upperNames = products.collect { it.name.toUpperCase() }
println "\n商品名称(大写): ${upperNames}"

这个示例展示了三种方法如何协同工作:each 用于展示数据、find 用于定位特定元素、collect 用于提取和转换数据。

5.2 输出结果

执行上述代码,你会看到如下输出:

=== 商品列表 ===
笔记本电脑 - ¥5999 - 电子产品
机械键盘 - ¥399 - 电子产品
办公椅 - ¥899 - 家具
咖啡机 - ¥1299 - 电器
显示器 - ¥1899 - 电子产品

最贵商品: 笔记本电脑 - ¥5999

商品总价: ¥10495

商品名称(大写): [笔记本电脑, 机械键盘, 办公椅, 咖啡机, 显示器]

6 方法对比速查表

方法 作用 返回值 典型使用场景
each 遍历每个元素 原集合本身 日志记录、打印输出、批量处理
find 查找第一个匹配项 单个元素或 null 搜索、验证、判断存在性
findAll 查找所有匹配项 新集合 过滤、筛选、多条件查询
collect 转换并收集结果 新集合 提取字段、类型转换、数据清洗

7 性能与最佳实践

在使用这三个方法时,有几个要点值得注意。首先,对于大型集合,如果只需要判断是否存在匹配元素,使用 any 方法比 find != null 更高效:

// 判断是否存在大于5的数字(推荐)
def exists = numbers.any { it > 5 }

// 判断价格是否有超过5000的商品(不推荐)
def hasExpensive = products.find { it.price > 5000 } != null

其次,findAllcollect 会创建新的集合,如果原集合很大,要注意内存占用问题。对于必须链式操作的场景,可以考虑使用扩展运算符来优化:

// 链式操作:先过滤再转换
def result = numbers.findAll { it % 2 == 0 }.collect { it * 10 }

// 等价写法,内存效率更高
def result2 = numbers.collect { if (it % 2 == 0) it * 10 }.findAll { it != null }

最后,当闭包逻辑较为复杂时,建议将逻辑抽取为独立的方法或使用有意义的参数名,提高代码可读性:

// 复杂逻辑抽取为方法
def isValidPrice = { product -> product.price > 0 && product.price < 100000 }
def validProducts = products.findAll(this.&isValidPrice)

8 小结

Groovy 的 eachfindcollect 三个方法构成了集合操作的基础工具链。each 负责逐个处理、find 负责精准定位、collect 负责批量转换。理解这三个方法的工作原理和使用场景,能够让你在处理数据时更加得心应手。建议在日常开发中多加练习,逐步形成对这些方法的直觉认知。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文