Groovy 集合操作:each、find、collect
Groovy 作为一门运行在 JVM 上的动态语言,对集合操作提供了极其便捷的支持。each、find 和 collect 是日常开发中最常用的三个方法,它们分别对应遍历、查找和转换三种核心操作。掌握这三个方法,能让你在处理数据时事半功倍。
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 实战场景:组合使用三种方法
在实际开发中,each、find 和 collect 很少单独使用,更多的是根据业务逻辑组合运用。以下是一个完整的示例场景。
5.1 场景描述
假设你有一个商品列表,每个商品是一个 Map,包含 name(名称)、price(价格)和 category(分类)三个字段。现在需要完成以下任务:
- 打印所有商品信息
- 找出价格最高的商品
- 计算所有商品的总价
- 将所有商品名称转换为大写
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
其次,findAll 和 collect 会创建新的集合,如果原集合很大,要注意内存占用问题。对于必须链式操作的场景,可以考虑使用扩展运算符来优化:
// 链式操作:先过滤再转换
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 的 each、find 和 collect 三个方法构成了集合操作的基础工具链。each 负责逐个处理、find 负责精准定位、collect 负责批量转换。理解这三个方法的工作原理和使用场景,能够让你在处理数据时更加得心应手。建议在日常开发中多加练习,逐步形成对这些方法的直觉认知。

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