Map (Отображения)
Отображения - структура данных, неупорядоченная коллекция пар "ключ-значение", в которой все ключи различны, а значение, связанное с заданным ключом, можно получить, обновить или удалить независимо от размера карты (отображения).
Отображение в Go представляет собой ссылку на хеш-таблицу, а его тип записывается как map[K]V
, где К и V являются типами его ключей и значений.
Все ключи в данном отображении имеют один и тот же тип, как и все значения имеют один и тот же тип, но тип ключей не обязан совпадать с типом значений. Тип ключа К должен быть сравниваемым с помощью оператора ==, чтобы отображение могло проверить, равен ли данный ключ одному из имеющихся в нем.
Создание отображений
Объеявление map выглядит следующим образом:
var RomanNums map[string]int
// где string - ключ, а int - значение
Однако так делать крайне нежелательно, так как мы не инициализировали его и при добавлении значений будут ошибки:
package main
import "fmt"
func main() {
var RomanNums map[string]int
RomanNums["I"] = 1
fmt.Println(RomanNums) // panic: assignment to entry in nil map
}
Поэтому есть второй, более правильный и надежный способ создания мапы в Go:
package main
import "fmt"
func main() {
// с помощью встроенной функции make:
ArabicNums := make(map[int]int)
// c помощь. использования литерала отображения:
RomanNums := map[string]int{
// пары ключ: значение указываются при необходимости
"I": 1,
"II": 2,
"III": 3,
"IV": 4,
"V": 5,
}
fmt.Println(ArabicNums) // map[]
fmt.Println(RomanNums) // map[I:1 II:2 III:3 IV:4 V:5]
}
Работа с отображениями
Обратиться к элементу отображения можно с помощью обычной индексации:
package main
import "fmt"
func main() {
RomanNums := map[string]int{
"I": 1,
"II": 2,
"III": 3,
"IV": 4,
"V": 5,
}
fmt.Println(RomanNums["IV"]) // 4
}
Если необъодимо удалить пару ключ: значение
, то необходимо воспользоваться функцией delete()
:
package main
import "fmt"
func main() {
RomanNums := map[string]int{
"I": 1,
"II": 2,
"III": 3,
"IV": 4,
"V": 5,
}
delete(RomanNums, "I")
fmt.Println(RomanNums) // map[II:2 III:3 IV:4 V:5]
}
Все эти операции безопасны, даже при условии, что элемент в отображении отсутствует: при использовании ключа, которого нет в отображении, поиск возвращает нулевое значение соответствующего типа:
package main
import "fmt"
func main() {
RomanNums := map[string]int{
"I": 1,
"II": 2,
"III": 3,
"IV": 4,
"V": 5,
}
delete(RomanNums, "I")
fmt.Println(RomanNums) // map[II:2 III:3 IV:4 V:5]
fmt.Println(RomanNums["I"]) // 0 (т.к. значение теперь отсутствует)
}
В приведенном примере мы видим, что если ключ в отображении отсутствует, то при обращении к значению по ключу будет возвращено нулевое значение соответствующего типа. Особенно это критично, если согласно логике нашей программы такое нулевое значение может иметь место. Как же понять, что ключ в отображении присутствует? Мы можем воспользоваться тестом из следующего примера:
package main
import "fmt"
func main() {
RomanNums := map[string]int{
"II": 2,
"III": 3,
"IV": 4,
"V": 5,
}
if value, inMap := RomanNums["I"]; inMap {
fmt.Println(value)
} // Условие не выполнится
if value, inMap := RomanNums["II"]; inMap {
fmt.Println(value) // 2
} // Условие выполнится
}
Итерирование по мапам
Для перечисления всех пар "ключ-значение" в отображении мы используем циклы по диапазону, аналогичные тем, которые мы использовали для массивов и срезов. Последовательные итерации приведенного ниже цикла присваивают переменным key
и value
значения из очередной пары "ключ-значение":
for key, value := range RomanNums {
fmt.Println(key, value)
}
Если нам необходимо получить только значения мапы, то вместо key
используем прочерк _
.
Кроме того, Go позволяет применить к отображению функцию len(), которая вернет количество пар "ключ-значение", хранящееся в отображении:
package main
import "fmt"
func main() {
RomanNums := map[string]int{
"II": 2,
"III": 3,
"IV": 4,
"V": 5,
}
fmt.Println(len(RomanNums)) // 4
}