1.字面量
在 Groovy 中,可以使用映射字面量语法创建映射(也称为关联数组)[:]
:
def map = [name: 'Gromit', likes: 'cheese', id: 1234]
assert map.get('name') == 'Gromit'
assert map.get('id') == 1234
assert map['name'] == 'Gromit'
assert map['id'] == 1234
assert map instanceof java.util.Map
def emptyMap = [:]
assert emptyMap.size() == 0
emptyMap.put("foo", 5)
assert emptyMap.size() == 1
assert emptyMap.get("foo") == 5
默认情况下,Map的Key是字符串:[a:1]
相当于['a':1]
.
如果您定义了一个名为a
的变量并且您希望 a
的值成为Map中的Key,这可能会造成混淆。如果是这种情况,那么您必须通过添加括号来转义密钥,如下例所示:
def a = 'Bob'
def ages = [a: 43]
assert ages['Bob'] == null // `Bob` 找不到
assert ages['a'] == 43 // 因为 `a` 已经被保存!
ages = [(a): 43] // 现在我们用括号对`a`进行转义
assert ages['Bob'] == 43 // 变量的值将会被找到
除了字面量之外,还可以获取Map的新副本,克隆它:
def map = [
simple : 123,
complex: [a: 1, b: 2]
]
def map2 = map.clone()
assert map2.get('simple') == map.get('simple')
assert map2.get('complex') == map.get('complex')
map2.get('complex').put('c', 3)
assert map.get('complex').get('c') == 3
生成的映射是原始映射的浅表副本,如前面的示例所示。
2.属性
Maps 也像 bean 一样,所以Map只要键是有效的 Groovy 标识符的字符串,您就可以使用属性表示法来获取/设置其中的项目:
def map = [name: 'Gromit', likes: 'cheese', id: 1234]
assert map.name == 'Gromit' // 可以使用 map.get('name') 替换
assert map.id == 1234
def emptyMap = [:]
assert emptyMap.size() == 0
emptyMap.foo = 5
assert emptyMap.size() == 1
assert emptyMap.foo == 5
注意:根据设计,map.foo
将总是在Map中寻找Key为foo
的值。这意味着不包含foo.class
类型的Key的Map上将返回空。如果你真的想知道这个类,那么你必须使用getClass()。
def map = [name: 'Gromit', likes: 'cheese', id: 1234]
assert map.class == null
assert map.get('class') == null
assert map.getClass() == LinkedHashMap // 这可能是你想要的Map类
map = [1 : 'a',
(true) : 'p',
(false): 'q',
(null) : 'x',
'null' : 'z']
assert map.containsKey(1) // 1不是一个标识符,所以使用时要注意。
assert map.true == null
assert map.false == null
assert map.get(true) == 'p'
assert map.get(false) == 'q'
assert map.null == 'z'
assert map.get(null) == 'x'
3.迭代
与Groovy 开发工具包中的往常一样,Map上的惯用迭代使用each
和eachWithIndex
方法。值得注意的是,使用Map字面量表示法创建的Map是有序的,也就是说,如果您对Map进行迭代,则可以保证条目将按照它们在映射中添加的顺序返回。
def map = [
Bob : 42,
Alice: 54,
Max : 33
]
// `entry` 是map的一项
map.each { entry ->
println "Name: $entry.key Age: $entry.value"
}
// `entry` 是map的一项, `i` 表示在map中的索引
map.eachWithIndex { entry, i ->
println "$i - Name: $entry.key Age: $entry.value"
}
// 或者,您可以直接使用 Key, value
map.each { key, value ->
println "Name: $key Age: $value"
}
// Key, value 并且 i 作为索引
map.eachWithIndex { key, value, i ->
println "$i - Name: $key Age: $value"
}
4.操作
4.1.添加和删除
使用以下方式向Map添加元素,可以使用put
方法、[]
运算符或putAll
:
def defaults = [1: 'a', 2: 'b', 3: 'c', 4: 'd']
def overrides = [2: 'z', 5: 'x', 13: 'x']
def result = new LinkedHashMap(defaults)
result.put(15, 't')
result[17] = 'u'
result.putAll(overrides)
assert result == [1: 'a', 2: 'z', 3: 'c', 4: 'd', 5: 'x', 13: 'x', 15: 't', 17: 'u']
可以通过调用以下clear方法来删除地图的所有元素:
def m = [1:'a', 2:'b']
assert m.get(1) == 'a'
m.clear()
assert m == [:]
使用映射字面量语法生成的映射使用对象equals
和hashcode
方法。这意味着您永远不应该使用哈希码会随时间变化的对象,否则您将无法取回相关值。
还值得注意的是,您永远不应该使用 GString
作为映射的键,因为 GString
的哈希码与等效项的哈希码不同String
:
def key = 'some key'
def map = [:]
def gstringKey = "${key.toUpperCase()}"
map.put(gstringKey,'value')
assert map.get('SOME KEY') == null
4.2.Keys, values 和 entries
我们可以检查视图中的keys, values, and entries:
def map = [1:'a', 2:'b', 3:'c']
def entries = map.entrySet()
entries.each { entry ->
assert entry.key in [1,2,3]
assert entry.value in ['a','b','c']
}
def keys = map.keySet()
assert keys == [1,2,3] as Set
不推荐对视图返回的值(无论是Map entry、Key还是Value)进行改变,因为操作的成功与否直接取决于被操作的Map的类型。特别是,Groovy依赖于来自JDK的集合,这些集合一般不保证可以通过keySet
、entrySet
或values
安全地进行操作。
4.3.过滤和搜索
Groovy 开发工具包包含与列表类似的过滤、搜索和收集方法:
def people = [
1: [name:'Bob', age: 32, gender: 'M'],
2: [name:'Johnny', age: 36, gender: 'M'],
3: [name:'Claire', age: 21, gender: 'F'],
4: [name:'Amy', age: 54, gender:'F']
]
def bob = people.find { it.value.name == 'Bob' } // 查找一个 entry
def females = people.findAll { it.value.gender == 'F' }
// 都返回entry,但你可以使用collection来检索年龄,例如
def ageOfBob = bob.value.age
def agesOfFemales = females.collect {
it.value.age
}
assert ageOfBob == 32
assert agesOfFemales == [21,54]
// 但也可以使用键/值对作为闭包的参数
def agesOfMales = people.findAll { id, person ->
person.gender == 'M'
}.collect { id, person ->
person.age
}
assert agesOfMales == [32, 36]
// `every` 如果所有entry都与条件匹配,则返回true
assert people.every { id, person ->
person.age > 18
}
// `any` 如果任何entry与条件匹配,则返回true
assert people.any { id, person ->
person.age == 54
}
4.4.分组
我们可以使用一些标准将列表分组到Map中:
assert ['a', 7, 'b', [2, 3]].groupBy {
it.class
} == [(String) : ['a', 'b'],
(Integer) : [7],
(ArrayList): [[2, 3]]
]
assert [
[name: 'Clark', city: 'London'], [name: 'Sharma', city: 'London'],
[name: 'Maradona', city: 'LA'], [name: 'Zhang', city: 'HK'],
[name: 'Ali', city: 'HK'], [name: 'Liu', city: 'HK'],
].groupBy { it.city } == [
London: [[name: 'Clark', city: 'London'],
[name: 'Sharma', city: 'London']],
LA : [[name: 'Maradona', city: 'LA']],
HK : [[name: 'Zhang', city: 'HK'],
[name: 'Ali', city: 'HK'],
[name: 'Liu', city: 'HK']],
]