Contents

Groovy中Map简介

1. 概述

Groovy  扩展了Java中的*Map * API以提供过滤、搜索和排序等操作的方法。它还提供了多种创建和操作Map的速记方式。

在本教程中,我们将了解 Groovy 使用Map的方式。

2. 创建 Groovy Map

*我们可以使用Map文字语法[k:v]*来创建Map。**基本上,它允许我们实例化一个映射并在一行中定义条目。 可以使用以下方法创建空Map:

def emptyMap = [:]

同样,可以使用以下方法实例化具有值的映射:

def map = [name: "Jerry", age: 42, city: "New York"]

请注意,**键没有被引号包围,并且默认情况下,Groovy 创建了java.util.LinkedHashMap 的一个实例。**我们可以使用as操作符来覆盖这个默认行为。

3. 添加项目

让我们从定义一个Map开始:

def map = [name:"Jerry"]

我们可以向Map添加一个键:

map["age"] = 42

然而,另一种更类似于 Javascript 的方式是使用属性表示法(点运算符):

map.city = "New York"

换句话说,Groovy 支持以类似 bean 的方式访问键值对。

在向Map添加新项目时,我们还可以使用变量而不是文字作为键:

def hobbyLiteral = "hobby"
def hobbyMap = [(hobbyLiteral): "Singing"]
map.putAll(hobbyMap)
assertTrue(hobbyMap.hobby == "Singing")
assertTrue(hobbyMap[hobbyLiteral] == "Singing")

首先,我们必须创建一个存储关键hobby的新变量。然后我们使用括号中的这个变量和映射文字语法来创建另一个映射。

4. 检索项目

文字语法或属性表示法可用于从Map中获取项目。

对于定义为的Map:

def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]

我们可以得到key对应的值:

assertTrue(map["name"] == "Jerry")

或者

assertTrue(map.name == "Jerry")

5. 移除项目

我们可以使用*remove()方法根据键从映射中删除任何条目,但**有时我们可能需要从映射中删除多个条目。我们可以通过使用minus()方法来做到这一点。*

minus()方法接受一个Map并在从底层映射中删除给定映射的所有条目后 返回一个新map

def map = [1:20, a:30, 2:42, 4:34, ba:67, 6:39, 7:49]
def minusMap = map.minus([2:42, 4:34]);
assertTrue(minusMap == [1:20, a:30, ba:67, 6:39, 7:49])

接下来,我们还可以根据条件删除条目。我们可以使用*removeAll()*方法实现这一点:

minusMap.removeAll{it -> it.key instanceof String}
assertTrue(minusMap == [1:20, 6:39, 7:49])

相反,要保留所有满足条件的条目,我们可以使用*retainAll()*方法:

minusMap.retainAll{it -> it.value % 2 == 0}
assertTrue(minusMap == [1:20])

6. 遍历条目

*我们可以使用*each()eachWithIndex()方法遍历条目

each()方法提供隐式参数,如entrykey和 value ,它们对应于当前Entry

除了Entry之外,*eachWithIndex()*方法还提供了一个索引。这两种方法都接受一个closures 作为参数。

在下一个示例中,我们将遍历每个entry。传递给each()方法的closures从隐式参数条目中获取键值对并打印出来:

map.each{entry -> println "$entry.key: $entry.value"}

接下来,我们将使用*eachWithIndex()*方法打印当前索引以及其他值:

map.eachWithIndex{entry, i -> println "$i $entry.key: $entry.value"}

也可以要求单独提供keyvalueindex

map.eachWithIndex{key, value, i -> println "$i $key: $value"}

7. 过滤

**我们可以使用find()findAll()grep()方法根据键和值过滤和搜索映射条目。

让我们首先定义一个Map来执行这些方法:

def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]

首先,我们看一下*find()方法,它接受一个Closure 并返回第一个匹配Closure 条件的Entry *:

assertTrue(map.find{it.value == "New York"}.key == "city")

类似地,findAll 也接受一个Closure , 但返回一个包含所有满足Closure 条件的键值对的Map

assertTrue(map.findAll{it.value == "New York"} == [city : "New York"])

如果我们更喜欢使用List,我们可以使用 grep而不是 findAll

map.grep{it.value == "New York"}.each{it -> assertTrue(it.key == "city" && it.value == "New York")}

我们首先使用 grep 查找具有值 New York 的条目。然后,为了演示返回类型是List,我们将遍历grep() 的结果。对于隐式参数中可用的列表中的每个*Entry *,我们将检查其是否为预期结果。

接下来,要确定Map中的所有项目是否满足条件,我们可以使用every, 它返回一个boolean

让我们检查一下Map中的所有值是否都是String类型:

assertTrue(map.every{it -> it.value instanceof String} == false)

同样,我们可以使用any来确定Map中的任何项目是否符合条件:

assertTrue(map.any{it -> it.value instanceof String} == true)

8. 转化与收集

有时,我们可能希望将映射中的条目转换为新值。使用collect()collectEntries()方法,可以将条目分别转换和收集到CollectionMap 中。

让我们看一些例子。给定一张员工 ID 和员工的Map:

def map = [
  1: [name:"Jerry", age: 42, city: "New York"],
  2: [name:"Long", age: 25, city: "New York"],
  3: [name:"Dustin", age: 29, city: "New York"],
  4: [name:"Dustin", age: 34, city: "New York"]]

我们可以使用*collect()*将所有员工的姓名收集到一个列表中:

def names = map.collect{entry -> entry.value.name}
assertTrue(names == ["Jerry", "Long", "Dustin", "Dustin"])

然后,如果我们对一组唯一的名称感兴趣,我们可以通过传递一个Collection对象来指定集合:

def uniqueNames = map.collect([] as HashSet){entry -> entry.value.name}
assertTrue(uniqueNames == ["Jerry", "Long", "Dustin"] as Set)

如果我们想将Map中的员工姓名从小写更改为大写,我们可以使用collectEntries。此方法返回转换值的映射:

def idNames = map.collectEntries{key, value -> [key, value.name]}
assertTrue(idNames == [1:"Jerry", 2:"Long", 3:"Dustin", 4:"Dustin"])

最后,还可以将collect方法与findfindAll方法结合使用来转换过滤结果:

def below30Names = map.findAll{it.value.age < 30}.collect{key, value -> value.name}
assertTrue(below30Names == ["Long", "Dustin"])

在这里,我们将找到所有年龄在 20-30 岁之间的员工,并将他们收集到一张Map中。

9. 分组

有时,我们可能希望根据条件将Map的某些项目分组为子Map。

**groupBy()方法返回一个映射映射,每个映射包含键值对,对于给定条件,它们的计算结果相同:

def map = [1:20, 2: 40, 3: 11, 4: 93]

def subMap = map.groupBy{it.value % 2}
assertTrue(subMap == [0:[1:20, 2:40], 1:[3:11, 4:93]])

**创建子图的另一种方法是使用subMap()。**它与*groupBy()*的不同之处在于它只允许基于键进行分组:

def keySubMap = map.subMap([1,2])
assertTrue(keySubMap == [1:20, 2:40])

在这种情况下,键 1 和 2 的条目将在新映射中返回,而所有其他条目都将被丢弃。

10. 排序

通常在排序时,我们可能希望根据键或值或两者对映射中的条目进行排序。Groovy 提供了可用于此目的的*sort()*方法。

给定一个Map:

def map = [ab:20, a: 40, cb: 11, ba: 93]

如果需要对键进行排序,我们将使用基于自然排序的无参数*sort()*方法:

def naturallyOrderedMap = map.sort()
assertTrue([a:40, ab:20, ba:93, cb:11] == naturallyOrderedMap)

或者我们可以使用*sort(Comparator)*方法来提供比较逻辑:

def compSortedMap = map.sort({k1, k2 -> k1 <=> k2} as Comparator)
assertTrue([a:40, ab:20, ba:93, cb:11] == compSortedMap)

接下来,*要对键、值或两者进行排序,我们可以为*sort()提供闭包条件

def cloSortedMap = map.sort({it1, it2 -> it1.value <=> it1.value})
assertTrue([cb:11, ab:20, a:40, ba:93] == cloSortedMap)