ZetCode

Groovy 映射

最后修改于 2025 年 3 月 20 日

Groovy 中的映射是键值集合,其中每个键映射到一个值。它们是动态的,允许您轻松添加或删除条目。本教程涵盖了创建映射、访问值以及执行遍历、排序和分组等操作,并附有示例来说明每个概念。

基础知识

映射使用方括号和冒号分隔的键值对定义。Groovy 提供了多种访问和修改它们的方法,使其能够高效地存储和检索数据。

MapBasics.groovy
def cts = [ sk: 'Slovakia', ru: 'Russia', de: 'Germany', no: 'Norway' ]

println cts['sk']
println cts.get('sk')
println cts.size()

cts.put('hu', 'Hungary')
println cts.containsKey('hu')

在此,cts 将国家代码映射到名称。您可以使用 ['key']get('key') 访问值,两者都返回 "Slovakia" for "sk"。size() 返回条目数。put('hu', 'Hungary') 添加一个新对,而 containsKey('hu') 检查 "hu" 是否存在,返回 true。这些方法展示了 Groovy 简洁的映射处理方式。

空映射和添加

您可以从一个空映射开始,然后动态地构建它,这是在数据逐步收集时的一种常见方法。

EmptyMap.groovy
def map = [:]
map['a'] = 1
map['b'] = 2
println map

[:] 创建一个空映射。使用 ['key'] = value,我们逐个添加条目。这将打印 [a:1, b:2],展示了映射如何灵活地增长,而无需预定义大小,这与数组不同。

遍历

遍历映射意味着迭代其条目。Groovy 提供了几种方法来循环遍历键和值,并可以选择包含索引以增加上下文。

MapTraversal.groovy
def capitals = [ Bratislava: 424207, Vilnius: 556723, Lisbon: 564657,
    Riga: 713016, Jerusalem: 780200, Warsaw: 1711324,
    Budapest: 1729040, Prague: 1241664, Helsinki: 596661,
    Tokyo: 13189000, Madrid: 3233527 ]

capitals.each { e -> 
    println "$e.key $e.value"
}

println "-----------------------------"

capitals.each { k, v -> 
    println "$k $v"
}

println "-----------------------------"

capitals.eachWithIndex { k, v, i -> 
    println "$i $k $v"
}

capitals 将城市名称映射到人口。第一个 each 使用 e 作为条目,访问 e.keye.value。第二个将参数命名为 kv 以示清晰。eachWithIndex 添加了 i,为每个条目编号。这些变体展示了 Groovy 灵活的迭代方式,让您可以选择最适合您需求的方式。

排序

对映射进行排序会根据键或值重新排列其条目。Groovy 的 sort 方法返回一个新的已排序映射,而原始映射保持不变,并支持通过闭包进行自定义比较。

MapSorting.groovy
def capitals = [ Bratislava: 424207, Vilnius: 556723, Lisbon: 564657,
    Riga: 713016, Jerusalem: 780200, Warsaw: 1711324,
    Budapest: 1729040, Prague: 1241664, Helsinki: 596661,
    Tokyo: 13189000, Madrid: 3233527 ]
    
println capitals.sort { it.key }
println capitals.sort { it.value }
println capitals.sort { a, b -> b.value <=> a.value }

sort { it.key } 按城市名称字母顺序排序。sort { it.value } 按人口升序排序。sort { a, b -> b.value <=> a.value } 使用宇宙飞船运算符 (<=>) 按人口降序排序。原始的 capitals 映射不会被更改;每次调用都会生成一个新的已排序映射,展示了 Groovy 函数式的排序方法。

查找

Groovy 的 findfindAll 方法搜索映射(通常是映射的映射),查找匹配条件的条目,分别返回第一个匹配项或所有匹配项。这些对于查询数据非常强大。

MapFinding.groovy
def users = [
   [fname: "Robert", lname: "Novak", salary: 1770],
   [fname: "John", lname:"Doe", salary: 1230],
   [fname: "Lucy", lname:"Novak", salary: 670],
   [fname: "Ben", lname:"Walter", salary: 2050],
   [fname: "Robin",lname: "Brown", salary: 2300],
   [fname: "Amy",lname: "Doe", salary: 1250],
   [fname: "Joe", lname:"Draker", salary: 1190],
   [fname: "Janet", lname:"Doe", salary: 980],
   [fname: "Peter",lname: "Novak", salary: 990],
   [fname:"Albert", lname:"Novak",salary: 193]
]

println users.find { u -> u.salary < 1000 }
println users.findAll { u -> u.salary < 1000 }

users 是一个映射列表,每个映射代表一个人。find { u -> u.salary < 1000 } 返回第一个薪资低于 1000 的用户(Lucy)。findAll 返回所有符合条件的此类用户(Lucy、Janet、Peter、Albert)。这些方法使用闭包来定义条件,使其能够适应您需要搜索的任何标准。

计数

countcountBy 方法根据条件对映射或映射列表中的条目进行计数,从而无需手动迭代即可快速汇总数据。

MapCounting.groovy
def users = [
   [fname: "Robert", lname: "Novak", salary: 1770],
   [fname: "John", lname:"Doe", salary: 1230],
   [fname: "Lucy", lname:"Novak", salary: 670],
   [fname: "Ben", lname:"Walter", salary: 2050],
   [fname: "Robin",lname: "Brown", salary: 2300],
   [fname: "Amy",lname: "Doe", salary: 1250],
   [fname: "Joe", lname:"Draker", salary: 1190],
   [fname: "Janet", lname:"Doe", salary: 980],
   [fname: "Peter",lname: "Novak", salary: 990],
   [fname:"Albert", lname:"Novak",salary: 193]
]

println users.count { u -> u.lname == 'Novak' || u.lname == 'Doe' }
println users.countBy { u -> u.salary < 1000 }

count { u -> u.lname == 'Novak' || u.lname == 'Doe' } 计算姓氏为“Novak”或“Doe”的用户(共 7 人)。countBy { u -> u.salary < 1000 } 按条件分组,返回一个包含 true(4 个)和 false(6 个)计数的映射。这些方法简化了数据分析,利用 Groovy 的闭包语法来实现自定义逻辑。

Collect & GroupBy

collect 将映射条目转换为新集合,而 groupBy 则根据标准将它们组织到子映射中。两者都是数据操作和重构的关键。

MapCollectGroup.groovy
def users = [
   [fname: "Robert", lname: "Novak", salary: 1770],
   [fname: "John", lname: "Doe", salary: 1230],
   [fname: "Lucy", lname: "Novak", salary: 670],
   [fname: "Ben", lname: "Walter", salary: 2050],
   [fname: "Robin",lname: "Brown", salary: 2300],
   [fname: "Amy",lname: "Doe", salary: 1250],
   [fname: "Joe", lname: "Draker", salary: 1190],
   [fname: "Janet", lname: "Doe", salary: 980],
   [fname: "Peter", lname: "Novak", salary: 990],
   [fname: "Albert", lname: "Novak",salary: 193]
]

println users.groupBy { it.lname }

println '--------------------------'

println users.collect { [it.fname, it.lname, it.salary * 1.1] }

println '--------------------------'

println users.collect { ["$it.fname $it.lname", it.salary * 1.1] }

groupBy { it.lname } 按姓氏将 users 分成子列表,创建一个以姓氏为键、匹配用户列表为值的映射。collect { [it.fname, it.lname, it.salary * 1.1] } 将每个用户转换为一个列表,其中薪资增加 10%。collect { ["$it.fname $it.lname", it.salary * 1.1] } 类似,但将姓名合并为单个字符串。这些操作凸显了 Groovy 灵活重塑数据的能力。

来源

Groovy 文档

本教程通过实际示例探讨了 Groovy 映射。

作者

我的名字是 Jan Bodnar,我是一名充满激情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在编程教学方面有超过八年的经验。

列出 所有 Groovy 教程