Comments 7
Закончился очередной курс вайтишников?
иногда хочется сделать ключом два значения, но не хочется объявлять дополнительно структуры. для этого может оказаться полезным в качестве ключа указать массив. например:
m := map[[2]any]string{}
m[[2]any{"user", 111}] = "Alice"
m[[2]any{"user", 222}] = "Bob"
println(m[[2]any{"user", 222}])
а если еще добавить в качестве мини-утилиты функцию
func key2(a, b any) [2]any {
return [2]any{a, b}
}
то можно писать так: m[key2("user", 111)] = "Alice"
иногда бывает полезным.
а то частенько встречал код, где делают мапы мапов %)
и тут я замечаю кое-что интересное :) не думал что ключом может быть массив из any
- т.к. any
ведь может быть и не сравнимым типом. попробовал заменить 222
на слайс - обламывается в рантайме. т.е. он не только при компиляции проверяет что ключ валидного типа. любопытно, спасибо!
а насчет ключа из двух значений - в скриптовых языках я обычно объединяю два значения в строчку чтобы сделать ключ - например, часто когда ключом надо координату сделать:
m[x .. ":" .. y] = 100
Про порядок ключей и печать мапы - можно уточнить, пакет fmt сам сортирует мапу по ключу при формировании mapString, поэтому обычный fmt.Print(m) всегда будет печатать "отсортированную" мапу. Видел подобный вопрос на каком-то из моков, возможно кому-то пригодится)
Добрый день! Вы так интересно описываете. Хотелось бы видеть у вас больше статей про Go.
"В каком порядке
range
перебирает элементы мэпы?" и подобные вопросы.Это не специфицировано. Другими словами "об этом не нужно думать или спрашивать". Спецификация языка упоминает в параграфе про мэпы:
A map is an unordered group of elements... (Мэпа - неупорядоченный набор элементов...)
и про
range
The iteration order over maps is not specified and is not guaranteed to be the same (порядок итерирования по мэпам не определен и нет гарантии что он будет всегда одинаков...)
Немного дополню информацией из второго издания книги "Go идиомы и паттерны проектирования" Джона Боднера:
При обходе мапы с помощью цикла
for-range
, порядок элементов будет меняться, иногда повторяясь. То есть три запускаfor-range
для одной и той же мапы обойдут элементы в разном порядке.Такое поведение обусловлено соображениями безопасности. В более ранних версиях Go порядок обхода ключей у отображений с одинаковыми элементами обычно (но не всегда) был одинаковым. Это порождало две проблемы:
1. Разработчики писали код с расчетом на фиксированный порядок обхода, но он иногда приводил к сбоям в самых неподходящий момент.
2. Если отображение всегда хеширует элементы в одни и те же значения и злоумышленнику известно, что сервер сохраняет пользовательские данные в виде отображения, то можно добиться реального замедления работы сервера с помощью DdoS-атаки на основе хеш-коллизий (Hash DoS), отправив серверу специально подготовленные данные, все ключи которых хешируются в одно и то же ведро (что за ведро?)
Чтобы устранить обе эти проблемы, разработчики языка Go внесли два изменения в реализацию отображения. Во-первых, они модифицировали хеш-алгоритм для отображений таким образом, чтобы при каждом создании переменной отображения генерировалось случайное число. Во-вторых, они сделали так, чтобы при каждом обходе отображения с помощью
for-range
порядок обхода элементов немного варьировался. Эти два изменения существенно усложнили проведение DoS-атаки на основе хеш-коллизий.Из этого правила есть одно исключение: чтобы упростить процесс отладки, функции форматирования, такие как
fmt.Println
, всегда выводят отображения в порядке возрастания ключей.
Вопросы по мэпам (map) в Go