Упражнение. Использование карт

Завершено

Карта в Go по сути представляет собой хэш-таблицу, которая является коллекцией пар "ключ-значение". Все ключи в карте должны иметь один и тот же тип. То же относится и к значениям. Однако для ключей и значений можно использовать разные типы. Например, ключи могут быть числами, а значения — строками. Для доступа к определенному элементу карты используется его ключ.

Объявление и инициализация карты

Для объявления карты необходимо использовать ключевое слово map. Затем определяются типы ключей и значений следующим образом: map[T]T. Например, если вы хотите создать карту, содержащую возраст учащихся, можно использовать следующий код:

package main

import "fmt"

func main() {
    studentsAge := map[string]int{
        "john": 32,
        "bob":  31,
    }
    fmt.Println(studentsAge)
}

При выполнении приведенного выше кода получается следующий результат:

map[bob:31 john:32]

Если вы не хотите инициализировать карту с элементами, можно использовать встроенную make() функцию для создания карты в предыдущем разделе. Чтобы создать пустую карту, можно использовать следующий код:

studentsAge := make(map[string]int)

Карты являются динамическими. После создания карты можно добавлять и удалять элементы карты, а также обращаться к ним. Давайте рассмотрим эти действия.

Добавить элементы

Чтобы добавить элементы, не нужно использовать встроенную функцию, как в случае со срезами. Работать с картами проще. Необходимо только определить ключ и значение. Если этой пары "ключ-значение" не существует, элемент добавляется в карту.

Давайте перепишем ранее использованный код и создадим карту с помощью функции make. Затем мы добавим элементы в карту. Можно использовать следующий код:

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    fmt.Println(studentsAge)
}

После выполнения кода вы получите тот же результат, что и ранее:

map[bob:31 john:32]

Обратите внимание, что мы добавили элементы в карту, которая была инициализирована. Но если вы попытаетесь сделать то же самое с картой nil, то получите сообщение об ошибке. Например, следующий код не будет работать:

package main

import "fmt"

func main() {
    var studentsAge map[string]int
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    fmt.Println(studentsAge)
}

При выполнении приведенного выше кода выводится следующая ошибка:

panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
        /Users/johndoe/go/src/helloworld/main.go:7 +0x4f
exit status 2

Чтобы избежать проблем при добавлении элементов в карту, сначала создайте пустую карту (не карту nil) с помощью функции make, как показано в предыдущем фрагменте кода. Это правило применяется только при добавлении элементов. При выполнении операций поиска, удаления или циклического перебора элементов карты nil паники в Go не возникнет. Мы проверим это на практике через некоторое время.

Доступ к элементам

Для доступа к элементам карты используется индекс (m[key]), как и для массивов и срезов. Ниже приведен простой пример, показывающий, как получить доступ к элементу:

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    fmt.Println("Bob's age is", studentsAge["bob"])
}

При использовании индекса в карте вы всегда получаете результат, даже если указанный ключ отсутствует в карте. При обращении к несуществующему элементу паники в Go не возникает. Вместо этого возвращается значение по умолчанию. Это поведение можно проверить с помощью следующего кода:

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    fmt.Println("Christy's age is", studentsAge["christy"])
}

При выполнении приведенного выше кода получается следующий результат:

Christy's age is 0

Во многих случаях отсутствие ошибки при доступе к элементу, которого не существует в карте, вполне справедливо. Но иногда необходимо знать, существует ли элемент. Индекс для карты в Go может возвращать два значения. Первое — это значение элемента. Второе — логический флаг, указывающий, существует ли ключ.

Чтобы устранить проблему с последним фрагментом кода, можно использовать следующий код:

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31

    age, exist := studentsAge["christy"]
    if exist {
        fmt.Println("Christy's age is", age)
    } else {
        fmt.Println("Christy's age couldn't be found")
    }
}

При выполнении приведенного выше кода получается следующий результат:

Christy's age couldn't be found

Используйте второй фрагмент кода, чтобы проверить, существует ли ключ в карте, перед тем как обращаться к нему.

Удаление элементов

Чтобы удалить элемент из карты, используйте встроенную функцию delete(). Ниже приведен пример удаления элемента из карты:

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    delete(studentsAge, "john")
    fmt.Println(studentsAge)
}

После выполнения кода вы увидите следующий результат:

map[bob:31]

Как было отмечено ранее, при попытке удалить несуществующий элемент паники в Go не возникает. Вот пример, иллюстрирующий это поведение:

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    delete(studentsAge, "christy")
    fmt.Println(studentsAge)
}

При выполнении кода вы не получите сообщение об ошибке, и результат будет следующим:

map[bob:31 john:32]

Циклический перебор элементов карты

Наконец, давайте посмотрим, как выполнить циклический перебор элементов карты, чтобы получить программный доступ ко всем элементам. Для этого можно использовать цикл на основе диапазона, как в следующем примере:

package main

import (
    "fmt"
)

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    for name, age := range studentsAge {
        fmt.Printf("%s\t%d\n", name, age)
    }
}

При выполнении приведенного выше кода получается следующий результат:

john    32
bob     31

Обратите внимание, что сведения о ключе и значении можно хранить в разных переменных. В данном случае ключ хранится в переменной name, а значение — в переменной age. Таким образом, range сначала возвращает ключ элемента, а затем значение элемента. Можно пропустить ключ или значение с помощью переменной _, как показано в следующем примере:

package main

import (
    "fmt"
)

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31

    for _, age := range studentsAge {
        fmt.Printf("Ages %d\n", age)
    }
}

Выводить возраст подобным образом не имеет смысла, однако бывают случаи, когда ключ элемента не требуется. Также можно использовать только ключи элементов, как в следующем примере:

package main

import (
    "fmt"
)

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31

    for name := range studentsAge {
        fmt.Printf("Names %s\n", name)
    }
}