在编程中,区间(Range)是高频使用的概念,常用于遍历、条件判断、数据筛选等场景。Java 中并无原生区间支持,需通过循环、集合或第三方工具类(譬如 Guava 的 Range )模拟,代码繁琐且可读性差。而 Groovy 内置了强大的区间特性,不仅支持数字、字符、日期等多种类型,还提供了丰富的操作方法,让范围相关操作变得简洁直观。
一、区间的定义:简洁直观的语法
Groovy 区间的核心语法是 start..end(闭区间)和 start..<end(左闭右开区间),支持多种数据类型,无需额外创建对象,直接通过语法糖即可快速定义。
1.1 数字区间(最常用场景)
数字区间支持整数、浮点数,涵盖 int、long、BigDecimal 等类型,满足数值范围需求。
groovy
// 1. 整数闭区间:包含 start 和 end(1 到 5,包含 5)
def intClosedRange = 1..5
println(intClosedRange) // 输出:1..5
println(intClosedRange.class) // 输出:class groovy.lang.IntRange(int 专用区间类)
// 2. 整数左闭右开区间:包含 start,不包含 end(1 到 4,不包含 5)
def intOpenRange = 1..<5
println(intOpenRange) // 输出:1..<5
println(intOpenRange.contains(5)) // 输出:false
// 3. 浮点数区间(支持小数范围)
def floatRange = 1.5..3.5
println(floatRange) // 输出:1.5..3.5
println(floatRange.contains(2.5)) // 输出:true
// 4. 反向数字区间(从大到小)
def reverseIntRange = 5..1
println(reverseIntRange) // 输出:5..1
reverseIntRange.each { println(it) } // 输出:5、4、3、2、1
1.2 字符区间
支持字符类型的区间,按 Unicode 编码顺序排列,适用于字母、符号等范围场景。
groovy
// 1. 小写字母区间(a 到 e,包含 e)
def lowerCharRange = 'a'..'e'
println(lowerCharRange) // 输出:a..e
lowerCharRange.each { println(it) } // 输出:a、b、c、d、e
// 2. 大写字母区间(A 到 D,左闭右开)
def upperCharRange = 'A'..<'D'
println(upperCharRange) // 输出:A..<D
println(upperCharRange.contains('D')) // 输出:false
// 3. 字符与数字混合区间(按 Unicode 编码排序)
def mixCharRange = '0'..'5'
println(mixCharRange) // 输出:0..5
mixCharRange.each { println(it) } // 输出:0、1、2、3、4、5
1.3 日期区间
支持 java.util.Date、java.time.LocalDate 等日期类型,无需手动计算日期差值,直接定义时间范围。
groovy
import java.time.LocalDate
import java.util.Date
// 1. LocalDate 区间(Java 8+ 推荐)
def startDate = LocalDate.of(2024, 1, 1)
def endDate = LocalDate.of(2024, 1, 5)
def dateRange = startDate..endDate
println(dateRange) // 输出:2024-01-01..2024-01-05
dateRange.each { println(it) } // 输出 2024-01-01 至 2024-01-05 每天
// 2. Date 区间(兼容旧版 API)
def startUtilDate = new Date(2024 - 1900, 0, 1) // Date 月份从 0 开始
def endUtilDate = new Date(2024 - 1900, 0, 3)
def utilDateRange = startUtilDate..endUtilDate
utilDateRange.each { println(it.format("yyyy-MM-dd")) } // 输出:2024-01-01、2024-01-02、2024-01-03
1.4 自定义类型区间(进阶)
只要自定义类实现 Comparable 接口(支持排序),即可创建该类型的区间,适配业务实体场景。
groovy
// 自定义 User 类,实现 Comparable 接口
class User implements Comparable<User> {
String name
Integer age
@Override
int compareTo(User other) {
this.age.compareTo(other.age) // 按年龄排序
}
}
// 创建 User 实例
def user1 = new User(name: "Alice", age: 20)
def user2 = new User(name: "Bob", age: 25)
// 自定义类型区间(按 age 排序)
def userRange = user1..user2
println(userRange) // 输出:User(Alice, 20)..User(Bob, 25)
// 验证区间包含关系(根据 compareTo 逻辑判断)
def user3 = new User(name: "Charlie", age: 22)
def user4 = new User(name: "Dave", age: 26)
println(userRange.contains(user3)) // 输出:true(20 ≤ 22 ≤ 25)
println(userRange.contains(user4)) // 输出:false(26 > 25)
二、区间的核心特性:不止于遍历
Groovy 区间不仅是"范围的载体",更内置了丰富的方法,支持包含判断、步长控制、转换集合、区间运算等操作,覆盖大部分场景需求。
2.1 包含判断:简洁的范围校验
通过 contains() 方法或 in 关键字(更符合自然语言),快速判断元素是否在区间内,替代 Java 中繁琐的 >= && <= 逻辑。
groovy
def ageRange = 18..35 // 成年人年龄区间
def scoreRange = 60..<100 // 及格分数区间(60-99)
// 1. in 关键字(推荐,可读性更强)
println(25 in ageRange) // 输出:true
println(59 in scoreRange) // 输出:false
println(99 in scoreRange) // 输出:true
// 2. contains() 方法
println(ageRange.contains(35)) // 输出:true(闭区间包含终点)
println(scoreRange.contains(100)) // 输出:false(左闭右开不包含终点)
// 3. 日期区间包含判断
def holidayRange = LocalDate.of(2024, 10, 1)..LocalDate.of(2024, 10, 7)
def checkDate = LocalDate.of(2024, 10, 5)
println(checkDate in holidayRange) // 输出:true
2.2 步长控制:灵活的间隔遍历
通过 step() 方法指定遍历间隔,无需手动计算索引,支持正向、反向步长,适用于分页、批量处理等场景。
groovy
// 1. 数字区间步长(默认步长为 1)
def numRange = 1..10
numRange.step(2) { println(it) } // 步长 2,输出:1、3、5、7、9
numRange.step(3) { println(it) } // 步长 3,输出:1、4、7、10
// 2. 反向区间步长
def reverseRange = 10..1
reverseRange.step(2) { println(it) } // 反向步长 2,输出:10、8、6、4、2
// 3. 日期区间步长(按天、月、年间隔)
def monthRange = LocalDate.of(2024, 1, 1)..LocalDate.of(2024, 12, 1)
// 步长 3 个月(通过 TemporalAmount 指定时间间隔)
monthRange.step(Period.ofMonths(3)) { println(it) }
// 输出:2024-01-01、2024-04-01、2024-07-01、2024-10-01
2.3 转换集合:无缝衔接集合操作
通过 toList()、toSet() 等方法将区间转为集合,直接复用 Groovy 集合的强大操作(过滤、映射、聚合等)。
groovy
def range = 1..10
// 1. 转为 List
def rangeList = range.toList()
println(rangeList) // 输出:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// 2. 转为 Set(自动去重,区间无重复元素,效果与 List 一致)
def rangeSet = range.toSet()
println(rangeSet) // 输出:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// 3. 结合集合方法筛选数据
def evenList = range.findAll { it % 2 == 0 } // 筛选偶数
println(evenList) // 输出:[2, 4, 6, 8, 10]
def squareList = range.collect { it * it } // 计算平方
println(squareList) // 输出:[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
def sum = range.sum() // 求和
println(sum) // 输出:55
2.4 区间运算:合并、交集与补集
Groovy 区间支持通过 plus()(合并)、intersect()(交集)等方法进行区间运算,满足复杂范围组合需求。
groovy
def range1 = 1..10
def range2 = 5..15
def range3 = 16..20
// 1. 区间合并(需连续或重叠,否则返回集合)
def mergedRange = range1 + range2 // 等价于 range1.plus(range2)
println(mergedRange) // 输出:1..15(重叠区间合并为连续区间)
def mergedRange2 = range1 + range3
println(mergedRange2) // 输出:[1..10, 16..20](非连续区间返回区间集合)
// 2. 区间交集(取共同部分)
def intersectionRange = range1.intersect(range2)
println(intersectionRange) // 输出:5..10
// 3. 区间补集(需基于更大范围)
def fullRange = 1..20
def complementRange = fullRange - range1 // 等价于 fullRange.minus(range1)
println(complementRange) // 输出:11..20
2.5 空区间与单元素区间
支持空区间(start > end 且非反向区间)和单元素区间(start == end),避免边界场景报错。
groovy
// 1. 单元素区间(start == end)
def singleRange = 5..5
println(singleRange) // 输出:5..5
println(singleRange.contains(5)) // 输出:true
singleRange.each { println(it) } // 输出:5
// 2. 空区间(start > end 且非反向定义)
def emptyRange = 10..5 // 注意:反向区间应显式定义为 10..5(非空),空区间需满足 start > end 且无反向意图
// 正确定义空区间的方式(左闭右开且 start == end)
def emptyOpenRange = 5..<5
println(emptyOpenRange) // 输出:5..<5
println(emptyOpenRange.isEmpty()) // 输出:true
emptyOpenRange.each { println(it) } // 无输出(空区间不执行遍历)
三、实战场景:区间处理的实际应用
Groovy 区间的简洁性和强大功能,在实际开发中能大幅简化代码,以下是高频实战场景。
3.1 遍历场景:替代传统循环
区间遍历比 for 循环更简洁,支持步长控制,适用于批量处理、循环执行等场景。
groovy
// 1. 批量创建用户(步长 1,遍历 1-10)
def users = []
(1..10).each { index ->
users << new User(name: "User$index", age: 18 + index)
}
println(users.size()) // 输出:10
// 2. 分页查询(步长 10,模拟分页索引)
def totalPages = 5
(1..totalPages).step(1) { page ->
println("查询第 $page 页数据")
// 执行分页查询逻辑:queryData(page, 10)
}
// 3. 延时执行(反向区间,模拟倒计时)
def countdown = 5..1
countdown.each { second ->
println("倒计时:$second 秒")
Thread.sleep(1000)
}
println("倒计时结束!")
3.2 条件判断:简化范围校验
替代 >= && <= 逻辑,让条件判断更直观,适用于参数校验、权限控制等场景。
groovy
// 1. 参数校验(年龄、分数范围)
def validateAge(Integer age) {
if (age in 0..120) {
println("年龄合法:$age")
} else {
println("年龄非法:$age")
}
}
validateAge(25) // 输出:年龄合法:25
validateAge(150) // 输出:年龄非法:150
// 2. 权限控制(角色等级范围)
def hasPermission(Integer roleLevel) {
// 管理员(3)、操作员(2)、普通用户(1),仅管理员和操作员有操作权限
return roleLevel in 2..3
}
println(hasPermission(3)) // 输出:true
println(hasPermission(1)) // 输出:false
// 3. 日期范围判断(活动有效期)
def isActivityValid(LocalDate date) {
def activityRange = LocalDate.of(2024, 6, 1)..LocalDate.of(2024, 6, 30)
return date in activityRange
}
println(isActivityValid(LocalDate.of(2024, 6, 15))) // 输出:true
println(isActivityValid(LocalDate.of(2024, 7, 1))) // 输出:false
3.3 数据筛选:精准过滤范围数据
结合集合操作,快速筛选出区间内的数据,适用于数据统计、报表生成等场景。
groovy
// 1. 筛选指定分数段的学生
class Student {
String name
Integer score
}
def students = [
new Student(name: "Alice", score: 85),
new Student(name: "Bob", score: 59),
new Student(name: "Charlie", score: 92),
new Student(name: "Dave", score: 76)
]
// 筛选及格(60-100)的学生
def passedStudents = students.findAll { it.score in 60..100 }
println(passedStudents.size()) // 输出:3
passedStudents.each { println("及格学生:${it.name},分数:${it.score}") }
// 2. 统计指定年龄段的用户数量
def userList = [
new User(name: "A", age: 22),
new User(name: "B", age: 35),
new User(name: "C", age: 40),
new User(name: "D", age: 28)
]
def youngUserCount = userList.count { it.age in 20..30 }
println("20-30 岁用户数量:$youngUserCount") // 输出:2
3.4 日期处理:简化时间范围操作
日期区间无需手动计算差值,直接定义时间范围,适用于日志查询、周期统计等场景。
groovy
import java.time.LocalDate
import java.time.temporal.ChronoUnit
// 1. 查询近 7 天的日志
def today = LocalDate.now()
def last7Days = today.minusDays(6)..today // 近 7 天(包含今天)
println("查询日志日期范围:$last7Days")
// 遍历近 7 天,执行日志查询
last7Days.each { date ->
def logDate = date.format(DateTimeFormatter.ISO_LOCAL_DATE)
println("查询 $logDate 的日志")
// 执行日志查询逻辑:queryLog(logDate)
}
// 2. 统计每月数据(按月份区间遍历)
def yearStart = LocalDate.of(2024, 1, 1)
def yearEnd = LocalDate.of(2024, 12, 1)
def monthlyRange = yearStart..yearEnd
// 按月份步长遍历
monthlyRange.step(Period.ofMonths(1)) { monthStart ->
def month = monthStart.format(DateTimeFormatter.ofPattern("yyyy-MM"))
println("统计 $month 的数据")
// 执行月度统计逻辑:statMonthlyData(month)
}
四、注意事项与进阶技巧
4.1 注意事项
-
区间类型兼容性:不同类型的区间不能直接运算(如数字区间与字符区间),需先转换为统一类型。
-
浮点数区间精度 :浮点数区间的
contains()方法需注意精度问题,建议结合BigDecimal或指定误差范围。groovydef floatRange = 0.1..0.3 println(floatRange.contains(0.2)) // 输出:true(无精度问题) println(floatRange.contains(0.199999999999)) // 输出:false(精度差异导致) -
日期区间的时区问题 :使用
Date类型时需注意时区一致性,推荐使用LocalDate/LocalDateTime(无时区依赖)。
4.2 进阶技巧
-
自定义区间步长逻辑:通过闭包自定义步长计算,满足复杂间隔需求。
groovy// 自定义步长:每次递增当前值的 2 倍(1, 2, 4, 8...) def customStepRange = 1..10 customStepRange.step { current, step -> def next = current * 2 if (next <= customStepRange.end) { println(current) return next // 返回下一个步长值 } return null // 结束遍历 } // 输出:1、2、4、8 -
区间与开关语句(switch)结合 :替代多个
case判断,简化范围分支逻辑。groovydef score = 85 switch (score) { case 90..100: println("优秀") break case 80..<90: println("良好") break case 60..<80: println("及格") break case 0..<60: println("不及格") break default: println("分数非法") } // 输出:良好
总结
Groovy 区间处理覆盖数字、字符、日期、自定义类型等多种场景,支持包含判断、步长控制、集合转换、区间运算等丰富操作。相比 Java,Groovy 区间不仅减少了模板代码,还提升了代码可读性和维护性,是遍历、条件判断、数据筛选等场景的"效率利器"。