Упражнение. Использование карт
Карта в 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)
}
}