1.字面量
在 Groovy 中,可以使用映射字面量语法创建映射(也称为关联数组)[:]:
def map = [name: 'Gromit', likes: 'cheese', id: 1234]assert map.get('name') == 'Gromit'assert map.get('id') == 1234assert map['name'] == 'Gromit'assert map['id'] == 1234assert map instanceof java.util.Mapdef emptyMap = [:]assert emptyMap.size() == 0emptyMap.put("foo", 5)assert emptyMap.size() == 1assert 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 == 1234def emptyMap = [:]assert emptyMap.size() == 0emptyMap.foo = 5assert emptyMap.size() == 1assert emptyMap.foo == 5
注意:根据设计,map.foo将总是在Map中寻找Key为foo的值。这意味着不包含foo.class类型的Key的Map上将返回空。如果你真的想知道这个类,那么你必须使用getClass()。
def map = [name: 'Gromit', likes: 'cheese', id: 1234]assert map.class == nullassert map.get('class') == nullassert map.getClass() == LinkedHashMap // 这可能是你想要的Map类map = [1 : 'a',(true) : 'p',(false): 'q',(null) : 'x','null' : 'z']assert map.containsKey(1) // 1不是一个标识符,所以使用时要注意。assert map.true == nullassert map.false == nullassert 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, valuemap.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' } // 查找一个 entrydef females = people.findAll { it.value.gender == 'F' }// 都返回entry,但你可以使用collection来检索年龄,例如def ageOfBob = bob.value.agedef agesOfFemales = females.collect {it.value.age}assert ageOfBob == 32assert agesOfFemales == [21,54]// 但也可以使用键/值对作为闭包的参数def agesOfMales = people.findAll { id, person ->person.gender == 'M'}.collect { id, person ->person.age}assert agesOfMales == [32, 36]// `every` 如果所有entry都与条件匹配,则返回trueassert people.every { id, person ->person.age > 18}// `any` 如果任何entry与条件匹配,则返回trueassert 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']],]
