Перейти к основному содержимому

Карты в Groovy

· 7 мин. чтения

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.