Groovy 区间:简洁高效的范围操作

在编程中,区间(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.Datejava.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 注意事项

  1. 区间类型兼容性:不同类型的区间不能直接运算(如数字区间与字符区间),需先转换为统一类型。

  2. 浮点数区间精度 :浮点数区间的 contains() 方法需注意精度问题,建议结合 BigDecimal 或指定误差范围。

    groovy 复制代码
      def floatRange = 0.1..0.3
      println(floatRange.contains(0.2))  // 输出:true(无精度问题)
      println(floatRange.contains(0.199999999999))  // 输出:false(精度差异导致)
  3. 日期区间的时区问题 :使用 Date 类型时需注意时区一致性,推荐使用 LocalDate/LocalDateTime(无时区依赖)。

4.2 进阶技巧

  1. 自定义区间步长逻辑:通过闭包自定义步长计算,满足复杂间隔需求。

    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
  2. 区间与开关语句(switch)结合 :替代多个 case 判断,简化范围分支逻辑。

    groovy 复制代码
      def 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 区间不仅减少了模板代码,还提升了代码可读性和维护性,是遍历、条件判断、数据筛选等场景的"效率利器"。

相关推荐
星火10241 天前
Groovy 实战:从微服务到 GUI 应用的快速集成
groovy
雨中飘荡的记忆4 天前
Java + Groovy计费引擎详解
java·groovy
星火10245 天前
Groovy:告别 Java 痛点,纵享丝滑编程
groovy
小小测试开发6 天前
JMeter JSR223预处理程序高级用法:解锁自动化测试的灵活性上限
开发语言·jmeter·groovy
星火10248 天前
【Groovy翻译系列三】Groovy应用集成
groovy
勤劳打代码21 天前
isar_flutter_libs 引发 Namespace not specified
android·flutter·groovy
安冬的码畜日常24 天前
【JUnit实战3_20】第十一章:用 Gradle 运行 JUnit 测试实战
测试工具·junit·单元测试·gradle·软件构建·groovy·junit5
ClassOps25 天前
Gradle Groovy 和 Kotlin kts 语法对比
android·kotlin·gradle·groovy
little_fat_sheep2 个月前
【Groovy】类和对象
groovy