在遍历 Map 时 , 并不是固定地从 0 号 Bucket 开始遍历 , 每次都是从一个随机值序号的 Bucket 开始遍历 , 并且是从这个 Bucket 的一个随机序号的 Cell 开始遍历 。
文章插图
Go 语言中的 map 是一种非常强大的数据结构 , 它允许我们快速地存储和检索键值对 。
【Go 语言 Map 如何顺序读取?】然而 , 当我们遍历 map 时 , 会有一个有趣的现象 , 那就是输出的键值对顺序是不确定的 。
现象先看一段代码示例:
package mainimport "fmt"func main() {m := map[string]int{"Apple":1,"banana": 2,"orange": 3,}for k, v := range m {fmt.Printf("key=%s, value=https://www.isolves.com/it/cxkf/yy/go/2023-05-19/%dn", k, v)}}
当我们多执行几次这段代码时 , 就会发现 , 输出的顺序是不同的 。原因首先 , Go 语言 map 的底层实现是哈希表 , 在进行插入时 , 会对 key 进行 hash 运算 。这也就导致了数据不是按顺序存储的 , 和遍历的顺序也就会不一致 。
第二 , map 在扩容后 , 会发生 key 的搬迁 , 原来落在同一个 bucket 中的 key , 搬迁后 , 有些 key 可能就到其他 bucket 了 。
而遍历的过程 , 就是按顺序遍历 bucket , 同时按顺序遍历 bucket 中的 key 。
搬迁后 , key 的位置发生了重大的变化 , 有些 key 被搬走了 , 有些 key 则原地不动 。这样 , 遍历 map 的结果就不可能按原来的顺序了 。
最后 , 也是最有意思的一点 。
那如果说我已经初始化好了一个 map , 并且不对这个 map 做任何操作 , 也就是不会发生扩容 , 那遍历顺序是固定的吗?
答:也不是 。
Go 杜绝了这种做法 , 主要是担心程序员会在开发过程中依赖稳定的遍历顺序 , 因为这是不对的 。
所以在遍历 map 时 , 并不是固定地从 0 号 bucket 开始遍历 , 每次都是从一个随机值序号的 bucket 开始遍历 , 并且是从这个 bucket 的一个随机序号的 cell 开始遍历 。
如何顺序读取如果希望按照特定顺序遍历 map , 可以先将键或值存储到切片中 , 然后对切片进行排序 , 最后再遍历切片 。
改造一下上面的代码 , 让它按顺序输出:
package mainimport ("fmt""sort")func main() {m := map[string]int{"apple":1,"banana": 2,"orange": 3,}// 将 map 中的键存储到切片中keys := make([]string, 0, len(m))for k := range m {keys = append(keys, k)}// 对切片进行排序sort.Strings(keys)// 按照排序后的顺序遍历 mapfor _, k := range keys {fmt.Printf("key=%s, value=https://www.isolves.com/it/cxkf/yy/go/2023-05-19/%dn", k, m[k])}}
在上面的代码中 , 首先将 map 中的键存储到一个切片中 , 然后对切片进行排序 。最后 , 按照排序后的顺序遍历 map 。这样就可以按照特定顺序输出键值对了 。
推荐阅读
- Redis 如何做内存优化?
- Redis 回收进程是如何工作的?
- 如何使用Python中的OCR技术将图像中的文本提取为可编辑文件?
- 如何为 APT 命令设置代理
- 你知道如何创建Linux用户和群组吗?
- Node Docker 官方镜像使用方法
- 如何制作动态图GIF?帮你轻松制作
- DeepFakes深度造假,如何识破“AI换脸术”?
- 如何在 Linux 中使用 split 命令拆分文本文件
- 笑果文化|笑果文化如何一步步自食恶果走向深渊