1. Overview
1.概述
Groovy extends the Map API in Java to provide methods for operations such as filtering, searching and sorting. It also provides a variety of shorthand ways to create and manipulate maps.
Groovy在Java中扩展了Map API,以提供过滤、搜索和排序等操作的方法。它还提供了多种速记方法来创建和操作地图。
In this tutorial, we’ll look at the Groovy way of working with maps.
在本教程中,我们将看看Groovy处理地图的方式。
2. Creating Groovy Maps
2.创建Groovy Maps
We can use the map literal syntax [k:v] for creating maps. Basically, it allows us to instantiate a map and define entries in one line.
我们可以使用map字面语法[k:v] 来创建map。基本上,它允许我们在一行中实例化一个map并定义条目。
An empty map can be created using:
可以用以下方法创建一个空地图。
def emptyMap = [:]
Similarly, a map with values can be instantiated using:
同样地,一个带有数值的地图可以用以下方式实例化。
def map = [name: "Jerry", age: 42, city: "New York"]
Notice that the keys aren’t surrounded by quotes, and by default, Groovy creates an instance of java.util.LinkedHashMap. We can override this default behavior by using the as operator.
注意,键没有被引号所包围,而在默认情况下,Groovy创建了一个java.util.LinkedHashMap的实例。我们可以通过使用as操作符来覆盖这个默认行为。
3. Adding Items
3.添加项目
Let’s start by defining a map:
让我们从定义一个地图开始。
def map = [name:"Jerry"]
We can add a key to the map:
我们可以在地图上添加一个键。
map["age"] = 42
However, another more Javascript-like way is using property notation (the dot operator):
然而,另一种更像Javascript的方式是使用属性符号(点运算符)。
map.city = "New York"
In other words, Groovy supports the access of key-value pairs in a bean like fashion.
换句话说,Groovy支持以类似Bean的方式访问键值对。
We can also use variables instead of literals as keys while adding new items to the map:
在向地图添加新项目时,我们也可以使用变量而不是字面数字作为键。
def hobbyLiteral = "hobby"
def hobbyMap = [(hobbyLiteral): "Singing"]
map.putAll(hobbyMap)
assertTrue(hobbyMap.hobby == "Singing")
assertTrue(hobbyMap[hobbyLiteral] == "Singing")
First, we have to create a new variable which stores the key hobby. Then we use this variable enclosed in parenthesis with the map literal syntax to create another map.
首先,我们必须创建一个新的变量,存储键hobby.,然后我们使用这个被括号包围的变量和map literal语法来创建另一个map。
4. Retrieving Items
4.检索项目
The literal syntax or the property notation can be used to get items from a map.
字面语法或属性符号可用于从地图中获取项目。
For a map defined as:
对于一个定义为的地图。
def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]
We can get the value corresponding to the key name:
我们可以得到与键name对应的值。
assertTrue(map["name"] == "Jerry")
or
或
assertTrue(map.name == "Jerry")
5. Removing Items
5.移除物品
We can remove any entry from a map based on a key using the remove() method, but sometimes we may need to remove multiple entries from a map. We can do this by using the minus() method.
我们可以使用remove()方法根据一个键从地图中删除任何条目,但是有时我们可能需要从一个地图中删除多个条目。我们可以通过使用minus()方法来做到这一点。。
The minus() method accepts a Map and returns a new Map after removing all the entries of the given map from the underlying map:
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])
Next, we can also remove entries based on a condition. We can achieve this using the removeAll() method:
接下来,我们还可以根据一个条件来删除条目。我们可以使用removeAll()方法实现这一目的。
minusMap.removeAll{it -> it.key instanceof String}
assertTrue(minusMap == [1:20, 6:39, 7:49])
Inversely, to retain all entries which satisfy a condition, we can use the retainAll() method:
反过来说,为了保留所有满足条件的条目,我们可以使用retainAll()方法。
minusMap.retainAll{it -> it.value % 2 == 0}
assertTrue(minusMap == [1:20])
6. Iterating Through Entries
6.遍历条目
We can iterate through entries using the each() and eachWithIndex() methods.
我们可以使用each() 和eachWithIndex()方法对条目进行迭代。
The each() method provides implicit parameters, like entry, key, and value, which correspond to the current Entry.
each()方法提供了隐式参数,如entry、key和value,它们与当前Entry相对应。
The eachWithIndex() method also provides an index in addition to Entry. Both the methods accept a Closure as an argument.
eachWithIndex()方法也在Entry之外提供了一个索引。这两个方法都接受一个Closure作为参数。
In the next example, we’ll iterate through each Entry. The Closure passed to the each() method gets the key-value pair from the implicit parameter entry and prints it:
在下一个例子中,我们将遍历每个Entry。传递给each()方法的Closure从隐含参数条目中获得键值对并打印出来。
map.each{entry -> println "$entry.key: $entry.value"}
Next, we’ll use the eachWithIndex() method to print the current index along with other values:
接下来,我们将使用eachWithIndex()方法来打印当前索引和其他值。
map.eachWithIndex{entry, i -> println "$i $entry.key: $entry.value"}
It’s also possible to ask the key, value, and index be supplied separately:
也可以要求分别提供key、value和index。
map.eachWithIndex{key, value, i -> println "$i $key: $value"}
7. Filtering
7.过滤
We can use the find(), findAll(), and grep() methods to filter and search for map entries based on keys and values.
我们可以使用find()、findAll()和grep()方法来过滤和搜索基于键和值的地图条目。
Let’s start by defining a map to execute these methods on:
让我们从定义一个地图开始,在上面执行这些方法。
def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]
First, we’ll look at the find() method, which accepts a Closure and returns the first Entry that matches the Closure condition:
首先,我们来看看find()方法,它接受一个Closure,并返回符合Closure条件的第一个Entry。
assertTrue(map.find{it.value == "New York"}.key == "city")
Similarly, findAll also accepts a Closure, but returns a Map with all the key-value pairs that satisfy the condition in the Closure:
类似地,findAll也接受一个Closure,但返回一个Map,其中包含满足Closure中条件的所有键值对。
assertTrue(map.findAll{it.value == "New York"} == [city : "New York"])
If we’d prefer to use a List though, we can use grep instead of findAll:
如果我们喜欢使用List,我们可以使用grep代替findAll。
map.grep{it.value == "New York"}.each{it -> assertTrue(it.key == "city" && it.value == "New York")}
We first used grep to find entries that have the value New York. Then, to demonstrate the return type is List, we’ll iterate through the result of grep(). For each Entry in the list which is available in the implicit parameter, we’ll check if its the expected result.
我们首先使用grep来找到有纽约这个值的条目。然后,为了证明返回类型是List,,我们将遍历grep()的结果。对于隐含参数中可用的列表中的每个Entry,我们将检查其是否是预期结果。
Next, to find out if all the items in a map satisfy a condition, we can use every, which returns a boolean.
接下来,为了找出一个地图中的所有项目是否满足一个条件,我们可以使用every,,它返回一个boolean。
Let’s check if all the values in the map are of type String:
让我们检查地图中的所有值是否都是String类型。
assertTrue(map.every{it -> it.value instanceof String} == false)
Similarly, we can use any to determine if any items in the map match a condition:
同样地,我们可以使用any来确定地图中的任何项目是否符合某个条件。
assertTrue(map.any{it -> it.value instanceof String} == true)
8. Transforming and Collecting
8.转化和收集
At times, we may want to transform the entries in a map into new values. Using the collect() and collectEntries() methods, it’s possible to transform and collect entries into a Collection or Map, respectively.
有时,我们可能希望将地图中的条目转化为新的值。使用collect()和collectEntries()方法,可以分别将条目转化为Collection或Map,。
Let’s look at some examples. Given a map of employee ids and employees:
我们来看看一些例子。给出一个雇员ID和雇员的地图。
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"]]
We can collect the names of all the employees into a list using collect():
我们可以使用collect()将所有雇员的名字收集到一个列表中。
def names = map.collect{entry -> entry.value.name}
assertTrue(names == ["Jerry", "Long", "Dustin", "Dustin"])
Then, if we’re interested in a unique set of names, we can specify the collection by passing a Collection object:
然后,如果我们对一个唯一的名字集感兴趣,我们可以通过传递一个Collection对象来指定这个集合。
def uniqueNames = map.collect([] as HashSet){entry -> entry.value.name}
assertTrue(uniqueNames == ["Jerry", "Long", "Dustin"] as Set)
If we want to change the employee names in the map from lowercase to uppercase, we can use collectEntries. This method returns a map of transformed values:
如果我们想把地图中的雇员名字从小写变成大写,我们可以使用collectEntries。这个方法返回一个转换后的值的地图。
def idNames = map.collectEntries{key, value -> [key, value.name]}
assertTrue(idNames == [1:"Jerry", 2:"Long", 3:"Dustin", 4:"Dustin"])
Finally, it’s also possible to use collect methods in conjunction with the find and findAll methods to transform the filtered results:
最后,也可以使用 collect方法与find和findAll方法结合起来来转换过滤后的结果。
def below30Names = map.findAll{it.value.age < 30}.collect{key, value -> value.name}
assertTrue(below30Names == ["Long", "Dustin"])
Here, we’ll find all employees between ages 20-30, and collect them into a map.
在这里,我们将找到所有年龄在20-30岁之间的员工,并将他们收集到一张地图中。
9. Grouping
9.分组
Sometimes, we may want to group some items of a map into submaps based on a condition.
有时,我们可能想根据一个条件将地图的某些项目归入子地图。
The groupBy() method returns a map of maps, and each map contains key-value pairs that evaluate to the same result for the given condition:
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]])
Another way of creating submaps is by using subMap(). It’s different than groupBy() in that it only allows for grouping based on the keys:
创建子图的另一种方法是使用subMap()。它与groupBy()不同,它只允许基于键进行分组。
def keySubMap = map.subMap([1,2])
assertTrue(keySubMap == [1:20, 2:40])
In this case, the entries for keys 1 and 2 are returned in the new map, and all the other entries are discarded.
在这种情况下,键1和键2的条目在新地图中被返回,所有其他条目被丢弃。
10. Sorting
10.分拣
Usually when sorting, we may want to sort the entries in a map based on key or value or both. Groovy provides a sort() method that can be used for this purpose.
通常在排序时,我们可能想根据键或值或两者来对地图中的条目进行排序。Groovy提供了一个sort()方法,可以用于这个目的。
Given a map:
给定一个地图。
def map = [ab:20, a: 40, cb: 11, ba: 93]
If sorting needs to be done on key, we’ll use the no-args sort() method, which is based on natural ordering:
如果需要对键进行排序,我们将使用no-args sort()方法,它是基于自然排序的。
def naturallyOrderedMap = map.sort()
assertTrue([a:40, ab:20, ba:93, cb:11] == naturallyOrderedMap)
Or we can use the sort(Comparator) method to provide comparison logic:
或者我们可以使用sort(Comparator)方法来提供比较逻辑。
def compSortedMap = map.sort({k1, k2 -> k1 <=> k2} as Comparator)
assertTrue([a:40, ab:20, ba:93, cb:11] == compSortedMap)
Next, to sort on either key, values, or both, we can supply a Closure condition to sort():
接下来,为了对键、值或两者进行排序,我们可以向sort()提供一个Closure条件。
def cloSortedMap = map.sort({it1, it2 -> it1.value <=> it1.value})
assertTrue([cb:11, ab:20, a:40, ba:93] == cloSortedMap)
11. Conclusion
11.结语
In this article, we learned how to create Maps in Groovy. Then we looked at different ways that we can add, retrieve and remove items from a map.
在这篇文章中,我们学习了如何在Groovy中创建Maps。然后,我们研究了从地图中添加、检索和删除项目的不同方法。
Finally, we covered Groovy’s out of the box methods for performing common operations, such as filtering, searching, transforming, and sorting.
最后,我们介绍了Groovy用于执行常见操作的开箱即用方法,如过滤、搜索、转换和排序。
As always, the examples covered in this article can be found over on GitHub.
一如既往,本文所涉及的例子可以在GitHub.上找到。