Map中的key为什么是无序的

2024-01-29 14:46:51 浏览数 (2)

为什么是无序的?

首先,我们先看下go的runtime中是如何实现map的迭代,以go 1.21.6为例,以下是关键部分,完整的源码位于src/runtime/map.go中:

代码语言:javascript复制
func mapiterinit(t *maptype, h *hmap, it *hiter) {
    // 省略部分

    // decide where to start
    var r uintptr
    if h.B > 31-bucketCntBits {
        r = uintptr(fastrand64())
    } else {
        r = uintptr(fastrand())
    }
    it.startBucket = r & bucketMask(h.B)
    it.offset = uint8(r >> h.B & (bucketCnt - 1))

    // iterator state
    it.bucket = it.startBucket

    // 省略部分
}

从上面的代码中可以看到,runtime确定map迭代的起始位置时使用伪随机数生成器fastrandfastrand64,使用哪个取决于哈希表的位数h.B,生成一个伪随机数r,然后再根据r来确定起始桶和偏移量。

因为每次迭代的起始位置都是不固定的,所以我们每次for range map的结构可能都是不一样的。

为什么要这样做?

在 Go 语言中,map 的键是无序的主要是为了维护 map 的高效性能和简化实现。以下是一些关于为什么选择无序键的考虑:

1.高效性能:无序键的 map 在插入、查找和删除等操作上具有高效性能。哈希表作为 map 的底层实现,能够提供近似 O(1) 的时间复杂度进行这些操作。无序性可以使哈希表更加灵活,更容易优化和实现。2.简化实现:无序性简化了 map 的实现。无需维护键的顺序,减少了数据结构的复杂性。这对于实现和维护 map 结构是有益的,使得代码更加清晰和高效。3.并发安全:无序键减少了并发访问时需要考虑的因素。在有序键的情况下,为了保持键的顺序,可能需要更复杂的数据结构或更多的同步机制。无序键简化了并发访问的实现。4.避免不确定性:有序键可能会引入不确定性,特别是在哈希表扩容时。在哈希表扩容时,键的顺序可能会发生变化,这可能会导致在遍历 map 时出现意外的结果。无序键可以避免这种不确定性。5.语言规范一致性:Go 语言的语法和规范中并没有规定 map 的键必须有序。因此,无序键符合语言设计的一致性和简洁性。

虽然 map 的键是无序的,但在 Go 1.12 版本及之后,map 的遍历顺序是有序的。这是通过一个有序的哈希表实现的,使得在遍历 map 时能够按照键的插入顺序进行。这种方式在一些应用场景中提供了方便,但在整体设计中仍然保持了 map 键的无序性。

声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)[1]进行许可,使用时请注明出处。 Author: mengbin[2] blog: mengbin[3] Github: mengbin92[4] cnblogs: 恋水无意[5] 腾讯云开发者社区:孟斯特[6]

References

[1] 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0): https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh [2] mengbin: mengbin1992@outlook.com [3] mengbin: https://mengbin.top [4] mengbin92: https://mengbin92.github.io/ [5] 恋水无意: https://www.cnblogs.com/lianshuiwuyi/ [6] 孟斯特: https://cloud.tencent.com/developer/user/6649301

0 人点赞