Go
June 17, 2023

Эффективный Go. Map (Хэш-таблица)

Хэш-таблицы (maps) — это удобная и мощная встроенная структура данных, которая связывает значения одного типа (ключ) со значениями другого типа (элемент или значение). Ключ может быть любого типа, для которого применим оператор сравнения, например целые числа, числа с плавающей запятой и комплексные числа, строки, указатели, интерфейсы (при условии, что динамический тип поддерживает равенство), структуры и массивы. Срезы нельзя использовать в качестве ключей хэш-таблицы, потому что для них нет возвожности сравнения. Как и срезы, хэш-таблицы содержат именно ссылки на базовую структуру данных. Если вы передаете хэш-таблицу функции, которая изменяет содержимое хэш-таблицы, изменения так же будут видны и в вызывающей программе.

Хэш-таблицы могут быть построены с использованием обычного синтаксиса составного литерала с парами ключ-значение, разделенными двоеточиями, поэтому их легко создавать во время инициализации.

var timeZone = map[string]int{
    "UTC":  0*60*60,
    "EST": -5*60*60,
    "CST": -6*60*60,
    "MST": -7*60*60,
    "PST": -8*60*60,
}

Присвоение и получение значений хэш-таблицы синтаксически выглядит так же, как и для массивов и срезов, за исключением того, что индекс не обязательно должен быть целым числом.

offset := timeZone["EST"]

Попытка получить значение хэш-таблицы с помощью ключа, которого нет в хэш-таблице, вернёт нулевое значение в соответствии с типом записей в хэш-таблицы. Например, если хэш-таблица содержит целые числа, поиск несуществующего ключа вернет 0. Набор (set) может быть реализован как хэш-таблица с типом значения bool. Установите для записи хэш-таблицы значение true, чтобы поместить значение в набор, а затем проверьте его с помощью простого обращения по индексу.

attended := map[string]bool{
    "Ann": true,
    "Joe": true,
    ...
}

if attended[person] { // будет ложным, если человека нет в хэш-таблице
    fmt.Println(person, "was at the meeting")
}

Иногда вам нужно отличать отсутствующую запись от нулевого значения. Есть ли запись для "UTC" или значение 0, потому что его вообще нет в хэш-таблице? Вы можете различать это используя форму множественного присвоения.

var seconds int
var ok bool
seconds, ok = timeZone[tz]

По очевидным причинам это называется идиомой «запятая ок». В этом примере, если присутствует tz, то переменной seconds будет присвоено соответвующее значение, только если ok истинно; если же нет и ok ложно, то seconds будет присвоено значение 0. Вот функция, которая объединяет пример выше с печатью отчета об ошибке:

func offset(tz string) int {
    if seconds, ok := timeZone[tz]; ok {
        return seconds
    }
    
    log.Println("unknown time zone:", tz)
    
    return 0
}

Чтобы проверить наличие значения в хэш-таблице, не беспокоясь о фактическом наличии значения, вы можете использовать пустой идентификатор (_) вместо переменной.

_, present := timeZone[tz]

Чтобы удалить запись хэш-таблицы, используйте встроенную функцию delete, аргументами которой являются хэш-таблица и ключ, которые необходимо удалить. Использовать её безопасно, даже если ключ уже отсутствует в хэш-таблице.

delete(timeZone, "PDT")  // Текущее стандартное время

Источник: https://go.dev/doc/effective_go#maps