Go 语言 map 如何顺序读取?

原文链接: Go 语言 map 如何顺序读取?

Go 语言中的 map 是一种非常强大的数据结构,它允许我们快速地存储和检索键值对。

然而,当我们遍历 map 时,会有一个有趣的现象,那就是输出的键值对顺序是不确定的。

现象

先看一段代码示例:

package main

import "fmt"

func main() {
    m := map[string]int{
        "apple":  1,
        "banana": 2,
        "orange": 3,
    }

    for k, v := range m {
        fmt.Printf("key=%s, value=%d\n", 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 main

import (
    "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)

    // 按照排序后的顺序遍历 map
    for _, k := range keys {
        fmt.Printf("key=%s, value=%d\n", k, m[k])
    }
}

在上面的代码中,首先将 map 中的键存储到一个切片中,然后对切片进行排序。

最后,按照排序后的顺序遍历 map。这样就可以按照特定顺序输出键值对了。

以上就是本文的全部内容,如果觉得还不错的话欢迎点赞转发关注,感谢支持。


参考文章:

推荐阅读:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言中的 map 和 slice 都是非常常用的数据结构,它们的实现都是基于底层的数组实现的。 ### Map Go语言中的 map 是一种无序的键值对集合,可以通过 key 来快速查找对应的 value。map 的底层实现是一个哈希表,可以通过哈希函数将 key 映射到一个桶中,每个桶中存储一个链表,用于解决哈希冲突。 当我们向 map 中添加一个元素时,会先根据 key 计算哈希值,然后将元素插入到对应的桶中。如果两个 key 的哈希值相同,那么它们会被插入到同一个桶中,采用链表方式解决冲突。 在对 map 进行遍历时,Go语言会按照 key 的哈希值顺序遍历所有的桶,然后依次遍历每个桶中的链表,因此 map 的遍历是无序的。 ### Slice Go语言中的 slice 是一种动态数组,可以根据需要动态增加或删除元素。slice 的底层实现是一个数组指针、一个长度和一个容量。slice 本身不存储元素,而是引用底层数组中的元素。 当我们向 slice 中添加一个元素时,如果当前 slice 的长度已经达到了容量上限,Go语言会自动重新分配一个更大的底层数组,并将原有元素复制到新的数组中。因此,slice 的容量也可以动态增加。 在对 slice 进行遍历时,可以使用 for range 循环,也可以使用下标方式访问 slice 中的元素。 需要注意的是,map 和 slice 都是引用类型,因此在函数调用时,传递的是引用,而不是值。如果修改了 map 或 slice 中的元素,会影响到原始的 map 或 slice。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值