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
}