GolangmapGolang
func main() {
    var m = make(map[int]int)
    m[1] = 1
    m[2] = 2
    m[3] = 3
    m[4] = 4
    m[5] = 5
    // m[6] = 6
    // m[7] = 7
    // m[8] = 8
    // m[9] = 9

    fmt.Println("first:")
    for k := range m {
        fmt.Println(k)
    }

    fmt.Println("second:")

    for k := range m {
        fmt.Println(k)
    }
}

运行上面代码,发现确实出现了两次不同顺序。

$ go run main.go
first:
1
2
3
4
5
second:
4
5
1
2
3
mapruntime.mapiterinitsrc/runtime/map.gobuckets
func mapiterinit(t *maptype, h *hmap, it *hiter) {
    ......
    // decide where to start
    r := uintptr(fastrand())
    if h.B > 31-bucketCntBits {
        r += uintptr(fastrand()) << 31
    }
    it.startBucket = r & bucketMask(h.B)
    it.offset = uint8(r >> h.B & (bucketCnt - 1))

    // iterator state
    it.bucket = it.startBucket
    ......
}

如果map是8个以内,也就是只有一个bucket的话,还是可以预测下一个顺序的值是什么,例如上面的运行,只要得到首个,就可以猜出来后面的顺序,但如果把map增加到9个元素,再运行呢,看看下图。

$ go run main.go
first:
3
4
5
1
2
8
9
6
7
second:
2
3
4
5
1
7
8
9
6

可以看到是很难预测下一个数值的,因为此时已经有了2个bucket,根据上述源码的实现,顺序相对于一个bucket就会变得不可预测。