ZetCode

Groovy 数组

最后修改于 2025 年 3 月 20 日

Groovy 中的数组是固定大小的集合,用于存储相同类型的元素。与列表不同,数组在创建后其大小无法更改。本教程将深入介绍如何创建数组、操作数组以及执行诸如排序、过滤和映射等常见任务,并附带实际示例指导您完成整个过程。

列表 vs 数组

在 Groovy 中,列表和数组在初始化时看起来可能相似,但它们是不同的。列表是动态的,可以增长或缩小,而数组的大小是固定的。由于它们的语法相似,您必须使用 as 或类型注解显式声明一个数组,以避免默认创建列表。

ListVsArray.groovy
def nums = [1, 2, 3, 4, 5]
println nums.class.name

def nums2 = [1, 2, 3, 4, 5] as int[]
println nums2.class.name

如果没有 as int[]nums 默认是一个 java.util.ArrayList,一种列表类型。通过添加 as int[]nums2 成了一个整数数组。您可以在右侧使用 as 或在左侧使用类型声明来指定数组类型,这为您定义数组提供了灵活性。

$ groovy ListVsArray.groovy
java.util.ArrayList
int[]

数组创建

Groovy 利用其简洁的语法提供了多种创建数组的方法。您可以使用 as 关键字和类列表初始化器,或者使用 new 关键字和显式类型及值。这两种方法都很直接,并且根据您的偏好常用。

ArrayCreation.groovy
def vals = [1, 2, 3, 4, 5] as int[]
println vals
println vals.length
println vals.size()

def words = new String[] {'sky', 'cloud', 'pen'}
println words

在这里,vals 是使用 as int[] 创建的,而 words 使用 new String[] 语法。lengthsize 都返回元素的数量,提供了检查数组长度的两种方法。这种双重性源于 Groovy 对 Java 兼容性的融合及其自身的增强。

$ groovy ArrayCreation.groovy
[1, 2, 3, 4, 5]
5
5
[sky, cloud, pen]

通过范围创建数组

Groovy 的范围运算符 (..) 提供了一种快速而优雅的方式来定义具有顺序值的数组。这在您想要连续序列而无需手动列出每个元素时,对于数字数组尤其有用。

RangeArray.groovy
int[] nums = -3..2
println nums

表达式 -3..2 生成一个从 -3 到 2(包括两端)的数组,共包含六个元素。这种简写是 Groovy 中的一个强大功能,它简化了数字范围的数组创建,并使您的代码更具可读性和简洁性。

$ groovy RangeArray.groovy
[-3, -2, -1, 0, 1, 2]

数组操作

Groovy 数组附带内置方法来执行常见操作,如查找大小、最小值、最大值、总和、平均值以及计数。这些方法使数组在数据操作方面具有通用性,无需复杂的循环或外部库。

ArrayOps.groovy
int[] nums = -4..10
println nums.size()
println nums.min()
println nums.max()
println nums.sum()
println nums.average()
println nums.count(-4)

String[] words = ['sky', 'waterfall', 'superintendent', 'war']
println words.max { it.size() }
println words.min { it.size() }

对于数字,size 获取长度,minmax 查找极值,sum 添加所有元素,average 计算平均值,count(-4) 计算 -4 的出现次数。对于字符串,maxmin 可以使用闭包,如 { it.size() },根据长度进行比较,这展示了 Groovy 在自定义标准方面的灵活性。

$ groovy ArrayOps.groovy
15
-4
10
45
3.0
1
superintendent
sky

元素访问

您可以使用方括号运算符 ([]) 或 getAt 方法来访问数组元素,这两种方式都支持正索引、负索引和范围。这提供了多种检索单个元素或数组切片的方法。

ElementAccess.groovy
int[] nums = -3..2
println nums[0]
println nums[1]
println nums.getAt(0)
println nums.getAt(-1)
println nums[-1..1]

String[] words = ['borrow', 'water', 'globe']
println words[0..2]

索引 0 获取第一个元素,而 -1 获取最后一个元素。范围 -1..1 从末尾向开头提取一个子数组,而 0..2 获取前三个元素。负索引是 Groovy 的一个便利功能,它从末尾开始倒数,使得在不手动计算偏移量的情况下处理数组更加容易。

$ groovy ElementAccess.groovy
-3
-2
-3
2
[2, 1, 0, -1]
[borrow, water, globe]

迭代

在 Groovy 中,可以使用传统的 for 循环或更具惯用性的 each 方法(支持闭包)来迭代数组。这些选项迎合了不同的编码风格,并提供了额外的功能,如索引跟踪。

Iteration.groovy
def nums = [1, 2, 3, 4, 5] as int[]
for (def e in nums) {
    println e
}

def words = ['sky', 'blue', 'war'] as String[]
words.each { println it }
words.eachWithIndex { e, i -> println "${i}: ${e}" }

for 循环迭代 nums 中的每个元素,每行打印一个。each 方法使用闭包对 words 执行相同的操作,其中 it 是默认参数。eachWithIndex 添加了索引 (i),让您可以跟踪位置,这对于带编号的输出或顺序很重要时很有用。

$ groovy Iteration.groovy
1
2
3
4
5
sky
blue
war
0: sky
1: blue
2: war

检查元素是否存在

contains 方法用于检查特定元素是否存在于数组中,并返回一个布尔值。这是一种无需编写循环或复杂逻辑即可测试成员资格的简单方法。

Contains.groovy
String[] words = ['sky', 'water', 'war', 'pen']
println words.contains('war')
println words.contains('small')

contains('war') 返回 true,因为 "war" 存在于数组中,而 contains('small') 返回 false,因为 "small" 不存在。此方法区分大小写且完全匹配,使其对于直接检查非常可靠。

$ groovy Contains.groovy
true
false

修改数组

Groovy 数组的大小是固定的,因此您无法直接删除元素,但可以使用 - 运算符修改它们或创建不包含某些元素的新数组。这反映了数组的固定长度,同时仍然允许灵活性。

ModifyArray.groovy
String[] words = ['sky', 'water', 'war', 'cup']
def words2 = words - 'war'
println words
println words2

words[0] = 'skylark'
println words

- 运算符不会改变 words,但会返回一个不包含 "war" 的新数组 words2。原始数组保持不变,显示了其固定大小的不可变性。将 words[0] = 'skylark' 赋值会替换一个元素,证明您可以在固定结构内更新值,但不能改变长度。

$ groovy ModifyArray.groovy
[sky, water, war, cup]
[sky, water, cup]
[skylark, water, war, cup]

打乱元素

shuffle 方法会就地随机化数组中元素的顺序,这意味着它会直接修改原始数组。这对于随机抽样或测试不同序列等任务很有用。

ShuffleArray.groovy
String[] words = ['sky', 'cloud', 'pen']
println words
words.shuffle()
println words
words.shuffle()
println words

每次调用 shuffle 都会随机重排 words。由于它是就地操作,每次都会修改原始数组,并且每次运行的输出都不同。这表明 Groovy 能够提供强大的、可变的数组操作,尽管它们的大小是固定的。

$ groovy ShuffleArray.groovy
[sky, cloud, pen]
[cloud, pen, sky]
[pen, sky, cloud]

排序

sort 方法会就地将数组元素按升序排列,修改原始数组。与 reverse 结合使用,它可以控制排序方向,使数组易于组织。

SortArray.groovy
String[] words = ['sky', 'water', 'war', 'pen']
println words.sort()
println '----------------------------'
println words.reverse()

sort 会将 words 按字母顺序排列,将其从初始顺序更改为 [pen, sky, war, water]。然后,reverse 将其翻转为降序。这两种操作都会直接修改数组,展示了 Groovy 如何将简洁性与直接操作相结合以进行排序任务。

$ groovy SortArray.groovy
[pen, sky, war, water]
----------------------------
[water, war, sky, pen]

过滤

grep 方法根据闭包中定义的条件过滤数组,并返回一个只包含匹配元素的新数组。这是一种在不编写显式循环的情况下提取子集的简洁方法。

FilterArray.groovy
String[] words = ['sky', 'water', 'war', 'pen']
def res = words.grep { it.startsWith('w') }
println res

在这里,grep { it.startsWith('w') } 只保留以 "w" 开头的元素,生成一个包含 "water" 和 "war" 的新数组。原始数组未被更改,并且闭包允许灵活的自定义过滤条件,展示了 Groovy 的函数式编程能力。

$ groovy FilterArray.groovy
[water, war]

映射

collect 方法将转换应用于每个元素,并返回一个包含结果的新数组。这是 Groovy 的数据映射方式,可用于根据原始数组转换或计算值。

MapArray.groovy
int[] nums = -4..10
def res = nums.collect { it * it }
println res

collect { it * it } 会将 nums 中的每个数字平方,创建一个包含结果的新数组。原始数组保持不变,并且闭包可以包含任何表达式,这使得 collect 成为 Groovy 中数据转换的通用工具。

$ groovy MapArray.groovy
[16, 9, 4, 1, 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

查找元素

Groovy 提供了 findfindAll 来查找符合条件的元素。find 返回第一个匹配项,而 findAll 返回所有匹配项,两者都使用闭包以实现灵活性。

FindArray.groovy
int[] nums = -4..10
def res = nums.findAll { it % 2 == 0 }
println res
res = nums.find { it > 0 }
println res

findAll { it % 2 == 0 } 将所有偶数收集到一个新数组中,而 find { it > 0 } 返回第一个正数 (1)。这些方法不会修改原始数组,并且是使用自定义逻辑进行搜索或过滤的理想选择,增强了 Groovy 的表达能力。

$ groovy FindArray.groovy
[-4, -2, 0, 2, 4, 6, 8, 10]
1

连接元素

join 方法将数组元素合并成一个字符串,使用指定的 separator。这对于创建格式化输出或序列化数据而不进行手动连接非常方便。

JoinArray.groovy
String[] words = ['sky', 'water', 'war']
def res = words.join(', ')
println res

join(', ')words 合并成一个逗号分隔的字符串。分隔符可以是任何字符串,提供了灵活性——使用 "" 表示没有分隔符,或使用类似 " - " 的内容表示其他样式。这是将数组转换为可读文本的简单而强大的方法。

$ groovy JoinArray.groovy
sky, water, war

Any 和 Every

anyevery 方法测试数组元素满足的条件,并返回布尔值。any 检查是否至少有一个元素满足条件,而 every 检查是否所有元素都满足条件,两者都使用闭包。

AnyEveryArray.groovy
int[] nums = -4..10
println nums.any { it > 0 }
println nums.every { it > 0 }

any { it > 0 } 返回 true,因为有些数字是正数,而 every { it > 0 } 返回 false,因为有些数字是负数或零。这些方法对于在不手动迭代的情况下验证数组的属性非常高效。

$ groovy AnyEveryArray.groovy
true
false

展平数组

flatten 方法接受一个嵌套数组(数组的数组),并将其转换为一个单一的、扁平的数组。在处理多维数据并希望将其作为单个序列进行处理时,这非常有用。

FlattenArray.groovy
int[][] nums = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
println nums
println nums.flatten()

nums 开始时是一个 3x3 的数组,而 flatten 将其转换为一个包含九个元素的单个数组。原始结构在展平之前被保留,结果是一个新数组,nums 保持不变,除非被重新赋值。这简化了对嵌套数据的处理。

$ groovy FlattenArray.groovy
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

来源

Groovy 文档

本教程通过实际示例探讨了 Groovy 数组。

作者

我的名字是 Jan Bodnar,我是一名热情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在教授编程方面拥有超过八年的经验。

列出 所有 Groovy 教程