1.字面量

在 Groovy 中,可以使用映射字面量语法创建映射(也称为关联数组)[:]

  1. def map = [name: 'Gromit', likes: 'cheese', id: 1234]
  2. assert map.get('name') == 'Gromit'
  3. assert map.get('id') == 1234
  4. assert map['name'] == 'Gromit'
  5. assert map['id'] == 1234
  6. assert map instanceof java.util.Map
  7. def emptyMap = [:]
  8. assert emptyMap.size() == 0
  9. emptyMap.put("foo", 5)
  10. assert emptyMap.size() == 1
  11. assert emptyMap.get("foo") == 5

默认情况下,Map的Key是字符串:[a:1]相当于['a':1].
如果您定义了一个名为a的变量并且您希望 a 的值成为Map中的Key,这可能会造成混淆。如果是这种情况,那么您必须通过添加括号来转义密钥,如下例所示:

  1. def a = 'Bob'
  2. def ages = [a: 43]
  3. assert ages['Bob'] == null // `Bob` 找不到
  4. assert ages['a'] == 43 // 因为 `a` 已经被保存!
  5. ages = [(a): 43] // 现在我们用括号对`a`进行转义
  6. assert ages['Bob'] == 43 // 变量的值将会被找到

除了字面量之外,还可以获取Map的新副本,克隆它:

  1. def map = [
  2. simple : 123,
  3. complex: [a: 1, b: 2]
  4. ]
  5. def map2 = map.clone()
  6. assert map2.get('simple') == map.get('simple')
  7. assert map2.get('complex') == map.get('complex')
  8. map2.get('complex').put('c', 3)
  9. assert map.get('complex').get('c') == 3

生成的映射是原始映射的浅表副本,如前面的示例所示。

2.属性

Maps 也像 bean 一样,所以Map只要键是有效的 Groovy 标识符的字符串,您就可以使用属性表示法来获取/设置其中的项目:

  1. def map = [name: 'Gromit', likes: 'cheese', id: 1234]
  2. assert map.name == 'Gromit' // 可以使用 map.get('name') 替换
  3. assert map.id == 1234
  4. def emptyMap = [:]
  5. assert emptyMap.size() == 0
  6. emptyMap.foo = 5
  7. assert emptyMap.size() == 1
  8. assert emptyMap.foo == 5

注意:根据设计,map.foo将总是在Map中寻找Key为foo的值。这意味着不包含foo.class类型的Key的Map上将返回空。如果你真的想知道这个类,那么你必须使用getClass()。

  1. def map = [name: 'Gromit', likes: 'cheese', id: 1234]
  2. assert map.class == null
  3. assert map.get('class') == null
  4. assert map.getClass() == LinkedHashMap // 这可能是你想要的Map类
  5. map = [1 : 'a',
  6. (true) : 'p',
  7. (false): 'q',
  8. (null) : 'x',
  9. 'null' : 'z']
  10. assert map.containsKey(1) // 1不是一个标识符,所以使用时要注意。
  11. assert map.true == null
  12. assert map.false == null
  13. assert map.get(true) == 'p'
  14. assert map.get(false) == 'q'
  15. assert map.null == 'z'
  16. assert map.get(null) == 'x'

3.迭代

与Groovy 开发工具包中的往常一样,Map上的惯用迭代使用eacheachWithIndex方法。值得注意的是,使用Map字面量表示法创建的Map是有序的,也就是说,如果您对Map进行迭代,则可以保证条目将按照它们在映射中添加的顺序返回。

  1. def map = [
  2. Bob : 42,
  3. Alice: 54,
  4. Max : 33
  5. ]
  6. // `entry` 是map的一项
  7. map.each { entry ->
  8. println "Name: $entry.key Age: $entry.value"
  9. }
  10. // `entry` 是map的一项, `i` 表示在map中的索引
  11. map.eachWithIndex { entry, i ->
  12. println "$i - Name: $entry.key Age: $entry.value"
  13. }
  14. // 或者,您可以直接使用 Key, value
  15. map.each { key, value ->
  16. println "Name: $key Age: $value"
  17. }
  18. // Key, value 并且 i 作为索引
  19. map.eachWithIndex { key, value, i ->
  20. println "$i - Name: $key Age: $value"
  21. }

4.操作

4.1.添加和删除

使用以下方式向Map添加元素,可以使用put方法、[]运算符或putAll

  1. def defaults = [1: 'a', 2: 'b', 3: 'c', 4: 'd']
  2. def overrides = [2: 'z', 5: 'x', 13: 'x']
  3. def result = new LinkedHashMap(defaults)
  4. result.put(15, 't')
  5. result[17] = 'u'
  6. result.putAll(overrides)
  7. assert result == [1: 'a', 2: 'z', 3: 'c', 4: 'd', 5: 'x', 13: 'x', 15: 't', 17: 'u']

可以通过调用以下clear方法来删除地图的所有元素:

  1. def m = [1:'a', 2:'b']
  2. assert m.get(1) == 'a'
  3. m.clear()
  4. assert m == [:]

使用映射字面量语法生成的映射使用对象equalshashcode方法。这意味着您永远不应该使用哈希码会随时间变化的对象,否则您将无法取回相关值。

还值得注意的是,您永远不应该使用 GString作为映射的键,因为 GString的哈希码与等效项的哈希码不同String

  1. def key = 'some key'
  2. def map = [:]
  3. def gstringKey = "${key.toUpperCase()}"
  4. map.put(gstringKey,'value')
  5. assert map.get('SOME KEY') == null

4.2.Keys, values 和 entries

我们可以检查视图中的keys, values, and entries:

  1. def map = [1:'a', 2:'b', 3:'c']
  2. def entries = map.entrySet()
  3. entries.each { entry ->
  4. assert entry.key in [1,2,3]
  5. assert entry.value in ['a','b','c']
  6. }
  7. def keys = map.keySet()
  8. assert keys == [1,2,3] as Set

不推荐对视图返回的值(无论是Map entry、Key还是Value)进行改变,因为操作的成功与否直接取决于被操作的Map的类型。特别是,Groovy依赖于来自JDK的集合,这些集合一般不保证可以通过keySetentrySetvalues安全地进行操作。

4.3.过滤和搜索

Groovy 开发工具包包含与列表类似的过滤、搜索和收集方法:

  1. def people = [
  2. 1: [name:'Bob', age: 32, gender: 'M'],
  3. 2: [name:'Johnny', age: 36, gender: 'M'],
  4. 3: [name:'Claire', age: 21, gender: 'F'],
  5. 4: [name:'Amy', age: 54, gender:'F']
  6. ]
  7. def bob = people.find { it.value.name == 'Bob' } // 查找一个 entry
  8. def females = people.findAll { it.value.gender == 'F' }
  9. // 都返回entry,但你可以使用collection来检索年龄,例如
  10. def ageOfBob = bob.value.age
  11. def agesOfFemales = females.collect {
  12. it.value.age
  13. }
  14. assert ageOfBob == 32
  15. assert agesOfFemales == [21,54]
  16. // 但也可以使用键/值对作为闭包的参数
  17. def agesOfMales = people.findAll { id, person ->
  18. person.gender == 'M'
  19. }.collect { id, person ->
  20. person.age
  21. }
  22. assert agesOfMales == [32, 36]
  23. // `every` 如果所有entry都与条件匹配,则返回true
  24. assert people.every { id, person ->
  25. person.age > 18
  26. }
  27. // `any` 如果任何entry与条件匹配,则返回true
  28. assert people.any { id, person ->
  29. person.age == 54
  30. }

4.4.分组

我们可以使用一些标准将列表分组到Map中:

  1. assert ['a', 7, 'b', [2, 3]].groupBy {
  2. it.class
  3. } == [(String) : ['a', 'b'],
  4. (Integer) : [7],
  5. (ArrayList): [[2, 3]]
  6. ]
  7. assert [
  8. [name: 'Clark', city: 'London'], [name: 'Sharma', city: 'London'],
  9. [name: 'Maradona', city: 'LA'], [name: 'Zhang', city: 'HK'],
  10. [name: 'Ali', city: 'HK'], [name: 'Liu', city: 'HK'],
  11. ].groupBy { it.city } == [
  12. London: [[name: 'Clark', city: 'London'],
  13. [name: 'Sharma', city: 'London']],
  14. LA : [[name: 'Maradona', city: 'LA']],
  15. HK : [[name: 'Zhang', city: 'HK'],
  16. [name: 'Ali', city: 'HK'],
  17. [name: 'Liu', city: 'HK']],
  18. ]