1. Обзор
Groovy расширяет Map
API на Java , предоставляя методы для таких операций, как фильтрация, поиск и сортировка . Он также предоставляет множество сокращенных способов создания карт и управления ими. `` ** **
В этом уроке мы рассмотрим способ работы с картами в Groovy.
2. Создание Groovy Maps
Мы можем использовать синтаксис литерала карты [k:v]
для создания карт. По сути, это позволяет нам создавать экземпляр карты и определять записи в одной строке.
Пустую карту можно создать с помощью:
def emptyMap = [:]
Точно так же можно создать экземпляр карты со значениями, используя:
def map = [name: "Jerry", age: 42, city: "New York"]
Обратите внимание, что ключи не заключены в кавычки, и по умолчанию Groovy создает экземпляр java.util.LinkedHashMap
. Мы можем переопределить это поведение по умолчанию, используя оператор as .
3. Добавление элементов
Начнем с определения карты:
def map = [name:"Jerry"]
Мы можем добавить ключ на карту:
map["age"] = 42
Однако другой способ, более похожий на Javascript, — это использование записи свойств (оператор точки):
map.city = "New York"
Другими словами, Groovy поддерживает доступ к парам ключ-значение по принципу bean-компонента.
Мы также можем использовать переменные вместо литералов в качестве ключей при добавлении новых элементов на карту:
def hobbyLiteral = "hobby"
def hobbyMap = [(hobbyLiteral): "Singing"]
map.putAll(hobbyMap)
assertTrue(hobbyMap.hobby == "Singing")
assertTrue(hobbyMap[hobbyLiteral] == "Singing")
Во-первых, мы должны создать новую переменную, в которой будет храниться ключевое хобби.
Затем мы используем эту переменную, заключенную в круглые скобки, с синтаксисом литерала карты, чтобы создать другую карту.
4. Получение предметов
Литеральный синтаксис или обозначение свойства можно использовать для получения элементов с карты.
Для карты, определенной как:
def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]
Мы можем получить значение, соответствующее имени ключа
:
assertTrue(map["name"] == "Jerry")
или же
assertTrue(map.name == "Jerry")
5. Удаление элементов
Мы можем удалить любую запись с карты на основе ключа, используя метод remove()
, но иногда нам может понадобиться удалить несколько записей с карты. Мы можем сделать это, используя метод minus()
.
Метод minus()
принимает карту
и возвращает новую карту
после удаления всех записей данной карты из базовой карты:
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])
И наоборот, чтобы сохранить все записи, удовлетворяющие условию, мы можем использовать метод continueAll()
:
minusMap.retainAll{it -> it.value % 2 == 0}
assertTrue(minusMap == [1:20])
6. Повторение записей
Мы можем перебирать записи, используя методы each()
и eachWithIndex()
.
Метод each()
предоставляет неявные параметры, такие как entry
, key
и value,
которые соответствуют текущему Entry
.
Метод eachWithIndex()
также предоставляет индекс в дополнение к Entry
. Оба метода принимают Closure
в качестве аргумента.
В следующем примере мы пройдемся по каждой записи.
Closure , переданный методу
each()
, получает пару ключ-значение из записи неявного параметра и печатает ее:
map.each{entry -> println "$entry.key: $entry.value"}
Далее мы будем использовать метод eachWithIndex()
для печати текущего индекса вместе с другими значениями:
map.eachWithIndex{entry, i -> println "$i $entry.key: $entry.value"}
Также можно попросить , чтобы ключ
, значение
и индекс предоставлялись отдельно:
map.eachWithIndex{key, value, i -> println "$i $key: $value"}
7. Фильтрация
Мы можем использовать методы find(), findAll()
и grep()
для фильтрации и поиска записей карты на основе ключей и значений.
Давайте начнем с определения карты для выполнения этих методов:
def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]
Во-первых, мы рассмотрим метод find()
, который принимает замыкание
и возвращает первую запись
, соответствующую условию замыкания :
assertTrue(map.find{it.value == "New York"}.key == "city")
Точно так же findAll
также принимает Closure,
но возвращает Map
со всеми парами ключ-значение, которые удовлетворяют условию в Closure
:
assertTrue(map.findAll{it.value == "New York"} == [city : "New York"])
Однако, если мы предпочитаем использовать список
, мы можем использовать grep
вместо findAll
:
map.grep{it.value == "New York"}.each{it -> assertTrue(it.key == "city" && it.value == "New York")}
Сначала мы использовали grep для поиска записей со значением New York. Затем, чтобы продемонстрировать, что тип возвращаемого значения — List,
мы пройдемся по результату grep().
Для каждой записи
в списке, которая доступна в неявном параметре, мы проверим, является ли это ожидаемым результатом.
Затем, чтобы узнать, все ли элементы на карте удовлетворяют условию, мы можем использовать каждый,
который возвращает логическое значение
.
Давайте проверим, все ли значения на карте имеют тип String
:
assertTrue(map.every{it -> it.value instanceof String} == false)
Точно так же мы можем использовать any
, чтобы определить, соответствуют ли какие-либо элементы на карте условию:
assertTrue(map.any{it -> it.value instanceof String} == true)
8. Преобразование и сбор
Иногда нам может понадобиться преобразовать записи на карте в новые значения. Используя методы collect()
и collectEntries()
, можно преобразовывать и собирать записи в коллекцию
или карту
соответственно.
Давайте посмотрим на некоторые примеры. Учитывая карту идентификаторов сотрудников и сотрудников:
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)
Если мы хотим изменить имена сотрудников на карте со строчных на прописные, мы можем использовать collectEntries
. Этот метод возвращает карту преобразованных значений:
def idNames = map.collectEntries{key, value -> [key, value.name]}
assertTrue(idNames == [1:"Jerry", 2:"Long", 3:"Dustin", 4:"Dustin"])
Наконец, также можно использовать методы collect
в сочетании с методами find
и findAll
для преобразования отфильтрованных результатов:
def below30Names = map.findAll{it.value.age < 30}.collect{key, value -> value.name}
assertTrue(below30Names == ["Long", "Dustin"])
Здесь мы найдем всех сотрудников в возрасте от 20 до 30 лет и соберем их на карту.
9. Группировка
Иногда нам может понадобиться сгруппировать некоторые элементы карты в подкарты в зависимости от условия.
Метод 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()
, который можно использовать для этой цели.
Дана карта:
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)
11. Заключение
В этой статье мы узнали, как создавать Map
в Groovy. Затем мы рассмотрели различные способы добавления, извлечения и удаления элементов с карты.
Наконец, мы рассмотрели стандартные методы Groovy для выполнения общих операций, таких как фильтрация, поиск, преобразование и сортировка.
Как всегда, примеры, рассмотренные в этой статье, можно найти на GitHub.