Почему range итерируется по map по разному

myMap := make(map[int]string)
    myMap[0]="dog"
    myMap[1] = "cat"
for _,val := range myMap{
    fmt.Println(val)
}

почему вывод при запусках может быть разный? Насколько мне известно, несортированная мапа работат при помощи хеша и берёт хеш по ключу и полученное значение это приращение к адресу начала памяти мапы, и по адресу мапы+приращение кладётся информация. Тогда, если функция взятия хеша одна и та же, то и вывод должен быть один и тот же, пусть не отсортированный по ключу(т.к. хеш от 0 может быть и больше чем хеш от 1), но тем не менее. Объясните почему не происходит как я ожидаю, а происходит так, что вывод может отличаться, при том же коде

Заранее спасибо


Ответы (3 шт):

Автор решения: CrazyElf

Документация:

The iteration order over maps is not specified and is not guaranteed to be the same from one iteration to the next.

Всё строго в соответствии с документацией.

→ Ссылка
Автор решения: Stanislav Volodarskiy

Это сделано специально, чтобы программист не опирался на определённый порядок итерации. Речь не идёт об изменении чего-то внутри мапы, сама итерация работает по разному на одной и той-же неизменной мапе:

package main
import "fmt"

func main() {
    myMap := make(map[int]int)
    for i := 0; i < 10; i++ {
        myMap[i] = i
    }
    for i := 0; i < 10; i++ {
        for _, val := range myMap {
            fmt.Print(" ", val)
        }
        fmt.Println()
    }
}
$ go run temp.go
 0 4 6 8 1 2 3 5 7 9
 0 4 6 8 7 9 1 2 3 5
 0 4 6 8 9 1 2 3 5 7
 3 5 7 9 1 2 6 8 0 4
 7 9 1 2 3 5 0 4 6 8
 2 3 5 7 9 1 4 6 8 0
 0 4 6 8 9 1 2 3 5 7
 1 2 3 5 7 9 0 4 6 8
 0 4 6 8 7 9 1 2 3 5
 9 1 2 3 5 7 0 4 6 8

P.S. Это спорное решение. Обычно программист предполагает что программа работает детерминировано. Я исключаю тут специальные средства вроде инициализации генератора случайных чисел текущим временем, или конкуренцию за ресурсы в многопоточной программе. Если вы пишите обычное вычисление, то вы привыкли думать что оно будет воспроизводиться раз за разом. Детерминированость предполагается для всех основных средств языка, это удобно.

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

→ Ссылка
Автор решения: Pak Uula

При инициализации итератора используется случайное число (runtime/map.go):

    // decide where to start
    r := uintptr(rand())
    it.startBucket = r & bucketMask(h.B)

Здесь it - указатель на итератор, h - хэш-таблица, startBucket - корзина хэш-таблицы, с которой начинается итерация.

→ Ссылка