Урок 5 из 8 · 20 мин · обновлено

Массивы, срезы и map

Чем массив отличается от среза, как добавлять элементы через append, перебирать их через range и хранить пары «ключ — значение» в map.

Массивы

Массив хранит заданное количество элементов одного типа. Длину указывают в квадратных скобках, и потом её не изменить:

Go
var days [3]string
days[0] = "пн"
fmt.Println(days, len(days))

scores := [3]int{90, 75, 60}
fmt.Println(scores[1])
Результат
[пн  ] 3
75

Индексы считаются с нуля: scores[1] — второй элемент. Незаполненные элементы получили нулевые значения — пустые строки. len() возвращает длину.

На практике массивы встречаются редко: заранее знать точное количество элементов удаётся нечасто.

Срезы

Срез — список, который может расти. Он объявляется так же, но без числа в скобках:

Go
tasks := []string{"купить хлеб", "позвонить маме"}
tasks = append(tasks, "оплатить интернет")
fmt.Println(tasks, len(tasks))
fmt.Println(tasks[0])
Результат
[купить хлеб позвонить маме оплатить интернет] 3
купить хлеб

append добавляет элементы в конец и возвращает новый срез. Поэтому результат всегда сохраняют обратно в переменную: tasks = append(tasks, …).

Из среза можно взять часть — от одного индекса до другого, не включая правую границу:

Go
nums := []int{10, 20, 30, 40, 50}
fmt.Println(nums[1:3])
fmt.Println(nums[:2], nums[3:])
Результат
[20 30]
[10 20] [40 50]

Если левую границу не указать, срез берётся с начала, если правую — до конца.

Перебор через range

for с range проходит по срезу и на каждом шаге выдаёт два значения — индекс и элемент:

Go
for i, t := range tasks {
	fmt.Println(i+1, t)
}
Результат
1 купить хлеб
2 позвонить маме
3 оплатить интернет

Если индекс не нужен, его пропускают через _:

Go
total := 0
for _, n := range nums {
	total += n
}
fmt.Println(total)
Результат
150

map: ключ и значение

map хранит пары «ключ — значение», как справочник: по названию товара быстро находится цена. В квадратных скобках — тип ключа, после них — тип значения:

Go
prices := map[string]int{
	"кофе": 180,
	"чай":  120,
}
prices["какао"] = 200
fmt.Println(prices["кофе"], len(prices))
Результат
180 3

Если ключа нет, map возвращает нулевое значение — для чисел это 0. Чтобы понять, есть ли ключ на самом деле, берут второе значение:

Go
p, ok := prices["сок"]
fmt.Println(p, ok)

if p, ok := prices["чай"]; ok {
	fmt.Println("Чай стоит", p)
}
Результат
0 false
Чай стоит 120

Во втором примере переменные объявлены прямо в if через точку с запятой: они видны только внутри этого условия.

Ещё несколько правил:

  • delete(prices, "какао") удаляет ключ;
  • пустую map создают через make(map[string]int);
  • порядок перебора map через range каждый раз может быть разным. Если нужен порядок, ключи сортируют отдельно.

Попробуйте сами. Есть срез заказов: []string{"чай", "кофе", "чай", "сок", "чай", "кофе"}. Посчитайте, сколько раз заказали каждый напиток, и выведите количество для чая, кофе, сока и какао.

Решение
Go
package main

import "fmt"

func main() {
	words := []string{"чай", "кофе", "чай", "сок", "чай", "кофе"}
	counts := make(map[string]int)
	for _, w := range words {
		counts[w]++
	}
	fmt.Println(counts["чай"], counts["кофе"], counts["сок"], counts["какао"])
}
Результат
3 2 1 0

Отсутствующий ключ возвращает 0, поэтому counts[w]++ работает сразу, без проверки: первый заказ превращает 0 в 1. Какао не заказывали — для него тоже 0.

Мини-тест: проверьте себя

Закрепите урок: несколько вопросов, разбор ответов — в конце.

  1. Чем срез []string отличается от массива [3]string?

  2. Почему пишут tasks = append(tasks, "чай"), а не просто append(tasks, "чай")?

  3. nums := []int{10, 20, 30, 40, 50}. Что вернёт nums[1:3]?

  4. В map prices нет ключа "сок". Что вернёт prices["сок"] для map[string]int?