«1. Обзор
Groovy расширяет Map API на Java, предоставляя методы для таких операций, как фильтрация, поиск и сортировка. Он также предоставляет множество сокращенных способов создания карт и управления ими.
В этой статье мы рассмотрим способ работы с картами в Groovy.
2. Создание карт Groovy
Мы можем использовать синтаксис литерала карты [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() предоставляет неявные параметры, такие как запись, ключ и значение, которые соответствуют текущей записи.
Метод eachWithIndex() также предоставляет индекс в дополнение к Entry. Оба метода принимают Closure в качестве аргумента.
В следующем примере мы перебираем каждую запись. Замыкание, переданное методу each(), получает пару ключ-значение из записи неявного параметра и выводит ее:
Затем мы используем метод eachWithIndex() для вывода текущего индекса вместе с другими значениями: ~ ~~
map.each{entry -> println "$entry.key: $entry.value"}
Также можно попросить, чтобы ключ, значение и индекс предоставлялись отдельно:
map.eachWithIndex{entry, i -> println "$i $entry.key: $entry.value"}
7. Фильтрация
map.eachWithIndex{key, value, i -> println "$i $key: $value"}
Мы можем использовать методы find(), findAll() и grep() для фильтровать и искать записи карты на основе ключей и значений.
Давайте начнем с определения карты, на которой будут выполняться эти методы:
Во-первых, мы рассмотрим метод find(), который принимает замыкание и возвращает первую запись, соответствующую условию замыкания:
def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]
~ ~~ Точно так же findAll также принимает замыкание, но возвращает карту со всеми парами ключ-значение, которые удовлетворяют условию в замыкании:
assertTrue(map.find{it.value == "New York"}.key == "city")
Если мы предпочитаем использовать список, мы можем использовать grep вместо findAll:
assertTrue(map.findAll{it.value == "New York"} == [city : "New York"])
Сначала мы использовали grep для поиска записей, имеющих значение New York. Затем, чтобы продемонстрировать, что тип возвращаемого значения — List, мы перебираем результат grep(). И для каждой записи в списке, которая доступна в неявном параметре, мы проверяем, является ли это ожидаемым результатом.
map.grep{it.value == "New York"}.each{it -> assertTrue(it.key == "city" && it.value == "New York")}
Затем, чтобы узнать, удовлетворяют ли все элементы карты условию, мы можем использовать каждый, который возвращает логическое значение.
Давайте проверим, все ли значения в карте имеют тип String:
Аналогично, мы можем использовать any, чтобы определить, соответствуют ли какие-либо элементы карты условию:
assertTrue(map.every{it -> it.value instanceof String} == false)
8. Преобразование и Сбор
assertTrue(map.any{it -> it.value instanceof String} == true)
«Иногда нам может понадобиться преобразовать записи на карте в новые значения. Используя методы collect() и collectEntries(), можно преобразовывать и собирать записи в коллекцию или карту соответственно.
Давайте рассмотрим несколько примеров.
Учитывая карту идентификаторов сотрудников и сотрудников:
Мы можем собрать имена всех сотрудников в список, используя collect():
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"]]
Далее, если нас интересует уникальный набор имен, мы можем указать коллекцию, передав объект Collection:
def names = map.collect{entry -> entry.value.name}
assertTrue(names == ["Jerry", "Long", "Dustin", "Dustin"])
Если мы хотим изменить имена сотрудников на карте со строчных на прописные, мы можем использовать collectEntries. Этот метод возвращает карту преобразованных значений:
def uniqueNames = map.collect([] as HashSet){entry -> entry.value.name}
assertTrue(uniqueNames == ["Jerry", "Long", "Dustin"] as Set)
Наконец, также можно использовать методы collect в сочетании с методами find и findAll для преобразования отфильтрованных результатов:
def idNames = map.collectEntries{key, value -> [key, value.name]}
assertTrue(idNames == [1:"Jerry", 2:"Long", 3:"Dustin", 4:"Dustin"])
Здесь мы сначала находим все сотрудников в возрасте от 20 до 30 лет и собрать их на карту.
def below30Names = map.findAll{it.value.age < 30}.collect{key, value -> value.name}
assertTrue(below30Names == ["Long", "Dustin"])
9. Группировка
Иногда нам может понадобиться сгруппировать некоторые элементы карты в подкарты на основе условия.
Метод groupBy() возвращает карту карт. И каждая карта содержит пары ключ-значение, которые дают один и тот же результат для заданного условия:
Другой способ создания подкарт — использование subMap(). В 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]])
В этом случае записи для ключей 1 и 2 возвращаются в новой карте, а все остальные записи отбрасываются.
def keySubMap = map.subMap([1,2])
assertTrue(keySubMap == [1:20, 2:40])
10. Сортировка
Обычно при сортировке мы можем захотеть отсортировать записи на карте по ключу, значению или тому и другому. Groovy предоставляет метод sort(), который можно использовать для этой цели.
Учитывая карту:
Если необходимо выполнить сортировку по ключу, используйте метод sort() без аргументов, основанный на естественном упорядочении:
def map = [ab:20, a: 40, cb: 11, ba: 93]
Или используйте sort(Comparator ) для обеспечения логики сравнения:
def naturallyOrderedMap = map.sort()
assertTrue([a:40, ab:20, ba:93, cb:11] == naturallyOrderedMap)
Далее, для сортировки либо по ключу, либо по значениям, либо по обоим, мы можем указать условие закрытия для sort():
def compSortedMap = map.sort({k1, k2 -> k1 <=> k2} as Comparator)
assertTrue([a:40, ab:20, ba:93, cb:11] == compSortedMap)
11. Заключение
def cloSortedMap = map.sort({it1, it2 -> it1.value <=> it1.value})
assertTrue([cb:11, ab:20, a:40, ba:93] == cloSortedMap)
Мы начали посмотрев, как мы можем создавать Карты в Groovy. Далее мы рассмотрели различные способы добавления, извлечения и удаления элементов с карты.
Позже мы рассмотрели методы выполнения общих операций, которые поставляются в Groovy из коробки. Они включали фильтрацию, поиск, преобразование и сортировку.
Как всегда, примеры, рассмотренные в статье, можно найти на GitHub.
«