Go语言核心36讲
郝林
《Go并发编程实战》作者,前轻松筹大数据负责人
立即订阅
24134 人已学习
课程目录
已完结 54 讲
0/4登录后,你可以任选4讲全文学习。
开篇词+学习路线 (3讲)
开篇词 | 跟着学,你也能成为Go语言高手
免费
预习篇 | 写给0基础入门的Go语言学习者
50 | 学习专栏的正确姿势
模块一:Go语言基础知识 (6讲)
01 | 工作区和GOPATH
02 | 命令源码文件
03 | 库源码文件
04 | 程序实体的那些事儿(上)
05 | 程序实体的那些事儿(中)
06 | 程序实体的那些事儿 (下)
模块二:Go语言进阶技术 (16讲)
07 | 数组和切片
08 | container包中的那些容器
09 | 字典的操作和约束
10 | 通道的基本操作
11 | 通道的高级玩法
12 | 使用函数的正确姿势
13 | 结构体及其方法的使用法门
14 | 接口类型的合理运用
15 | 关于指针的有限操作
16 | go语句及其执行规则(上)
17 | go语句及其执行规则(下)
18 | if语句、for语句和switch语句
19 | 错误处理(上)
20 | 错误处理 (下)
21 | panic函数、recover函数以及defer语句 (上)
22 | panic函数、recover函数以及defer语句(下)
模块三:Go语言实战与应用 (27讲)
23 | 测试的基本规则和流程 (上)
24 | 测试的基本规则和流程(下)
25 | 更多的测试手法
26 | sync.Mutex与sync.RWMutex
27 | 条件变量sync.Cond (上)
28 | 条件变量sync.Cond (下)
29 | 原子操作(上)
30 | 原子操作(下)
31 | sync.WaitGroup和sync.Once
32 | context.Context类型
33 | 临时对象池sync.Pool
34 | 并发安全字典sync.Map (上)
35 | 并发安全字典sync.Map (下)
36 | unicode与字符编码
37 | strings包与字符串操作
38 | bytes包与字节串操作(上)
39 | bytes包与字节串操作(下)
40 | io包中的接口和工具 (上)
41 | io包中的接口和工具 (下)
42 | bufio包中的数据类型 (上)
43 | bufio包中的数据类型(下)
44 | 使用os包中的API (上)
45 | 使用os包中的API (下)
46 | 访问网络服务
47 | 基于HTTP协议的网络服务
48 | 程序性能分析基础(上)
49 | 程序性能分析基础(下)
尾声与思考题答案 (2讲)
尾声 | 愿你披荆斩棘,所向无敌
新年彩蛋 | 完整版思考题答案
Go语言核心36讲
登录|注册

09 | 字典的操作和约束

郝林 2018-08-31
至今为止,我们讲过的集合类的高级数据类型都属于针对单一元素的容器。
它们或用连续存储,或用互存指针的方式收纳元素,这里的每个元素都代表了一个从属某一类型的独立值。
我们今天要讲的字典(map)却不同,它能存储的不是单一值的集合,而是键值对的集合。
什么是键值对?它是从英文 key-value pair 直译过来的一个词。顾名思义,一个键值对就代表了一对键和值。
注意,一个“键”和一个“值”分别代表了一个从属于某一类型的独立值,把它们两个捆绑在一起就是一个键值对了。
在 Go 语言规范中,应该是为了避免歧义,他们将键值对换了一种称呼,叫做:“键 - 元素对”。我们也沿用这个看起来更加清晰的词来讲解。

知识前导:为什么字典的键类型会受到约束?

Go 语言的字典类型其实是一个哈希表(hash table)的特定实现,在这个实现中,键和元素的最大不同在于,键的类型是受限的,而元素却可以是任意类型的。
如果要探究限制的原因,我们就先要了解哈希表中最重要的一个过程:映射。
你可以把键理解为元素的一个索引,我们可以在哈希表中通过键查找与它成对的那个元素。
键和元素的这种对应关系,在数学里就被称为“映射”,这也是“map”这个词的本意,哈希表的映射过程就存在于对键 - 元素对的增、删、改、查的操作之中。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Go语言核心36讲》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(34)

  • 江山如画
    非原子操作需要加锁, map并发读写需要加锁,map操作不是并发安全的,判断一个操作是否是原子的可以使用 go run race 命令做数据的竞争检测

    作者回复: 说得很对

    2018-09-01
    1
    64
  • 杨赛
    希望有点深度。
    2018-09-01
    33
  • 张民
    郝大,有个疑问:文中描述“也就是说,字典不会存储任何键值,只会存储它们的哈希值。“ 但是在查找的时候,根据键值的哈希找到后,又去比较键值,防止哈希碰撞。但是键值没有存储怎么比较?

    作者回复: 哈希桶里的结构是,“键的哈希值-内部结构”对的集合,这个内部结构的结构是“键1元素1键2元素2键3元素3”,是一块连续的内存。在通过键的哈希值定位找到哈希桶和那个“键的哈希值-内部结构”对之后,就开始在这个内部结构里找有没有这个键。后边的事情你应该都知道了。

    2018-08-31
    31
  • zs阿帅
    能不能在深剖析下go map底层实现机制,配合源码讲解
    2018-08-31
    18
  • Laughing
    字典并发并不安全,对字典操作最好用1.9之后新加入的sync.Map,如果不用的话需要对goroutine加读写锁了。
    2018-08-31
    13
  • melon
    goroutine 并发读写 map 会触发 fatal error: concurrent map read and map write 这样的运行时错误 ,可以试一下下面这个例子看效果:
    package main

    import (
        "fmt"
        "time"
    )

    func main() {
        m := map[int]string {
            1 : "haha",
        }

        go read(m)
        time.Sleep(time.Second)
        go write(m)
        time.Sleep(30*time.Second)
        fmt.Println(m)
    }

    func read(m map[int]string) {
        for {
            _ = m[1]
            time.Sleep(1)
        }
    }

    func write(m map[int]string) {
        for {
            m[1] = "write"
            time.Sleep(1)
        }
    }

    可以通过 go run -race 来检测代码是否存在这种潜在的并发读写
    通过 sync.Map 或自己使用sync.RWMutex自己实现并发互斥逻辑
    2018-08-31
    11
  • hiyanxu
    在map的创建中:
    直接声明“var m map[int]string”的形式声明出来的m为nil;
    采用make函数创建的map不为nil,可以进行添加键值对的操作。
    2018-12-16
    10
  • 慢熊胖胖跑
    Go 语言的字典类型其实是一个哈希表(hash table)的特定实现。
    ————————————————-
    为啥都是hash表了 那为啥map range遍历key还是无序的呢?
    2018-09-15
    1
    5
  • balancer
    希望后面层层深入,深入到,go的并发实现原理核内存管理(听到有开发者遇到内存持续不降的问题,这一点我猜测是gc上面)、错误处理、运行时多态、模板
    2018-08-31
    4
  • 我不会算法
    nil的map,既然不能添加key-element,是否也就意味着这样的map是没有任何意义的?在使用中,应避免用`var m map[string]int` 这种方式来声明map呢?

    作者回复: 可以对m直接用索引表达式添加啊。

    2018-10-28
    3
  • 斗转星移
    "也就是说,字典不会存储任何键值,只会存储它们的哈希值。" 我也觉得这个说法不严谨,既然,"内部结构"是将键值对捆绑存的,那字典就是存了键,除非"内部结构"不属于字典? 可能没理解到,请郝大指点迷津

    作者回复: 好吧,可以说,不会独立存储键的值。

    2018-09-04
    3
  • extraterrestrial!!
    函数类型为啥不能判等呢,函数头相同就认为相等不行么?

    作者回复: 这是Go语言的规定,没必要纠结,你完全用类型断言去判断啊。

    2018-09-04
    3
  • Geek_1ed70f
    不知道是理解能力差,还是基础差,以前只会随便用用,现在读您的文章,读完第一遍似懂非懂,然后用一下午时间翻源码,看解释,弄原理...再回头一读豁然开朗,.....精学了,但是好费时间啊,~~~~~老师如何评价进度与学习成本的取舍呢

    作者回复: 你这个时间成本是值得的。真正的学习没有捷径啊。

    2019-02-07
    2
  • 顾骨
    “哈希桶里的结构是,“键的哈希值-内部结构”对的集合,这个内部结构的结构是“键1元素1键2元素2键3元素3”,是一块连续的内存。在通过键的哈希值定位找到哈希桶和那个“键的哈希值-内部结构”对之后,就开始在这个内部结构里找有没有这个键。后边的事情你应该都知道了。”

    引用下,内部结构的结构是“键1元素1键2元素2键3元素3”的存储方式,那怎么知道键的宽度是多少呢?会不会将键和元素一起当做一个键来处理了?
    2018-10-18
    2
  • kuzan
    go里面有没有分段锁的字典实现呢

    作者回复: 没有,sync.Map也不是分段锁实现的,如果想看分段锁实现可以看我的《Go并发编程实战》第二版中的例子。

    2018-10-09
    2
  • SuperP ❤ 飝
    一个哈希表会持有一定数量的桶(bucket),那个新增加一个键值得时候,怎么去划分桶的?

    作者回复: 我记得文章里讲了。

    2018-09-13
    2
  • 谁谁
    go中有接口类型,java中也有,python中的接口好像是说要定义成抽象基类!能说下这个接口到底是个什么东西吗?不太了解!
    2018-09-03
    2
  • 小小笑儿
    不是,要并发安全的应该用sync.Map
    2018-08-31
    2
  • Timo
    哈哈,看了评论才知道底层具体是如何存储字典的。
    因为有可能哈希值相同,所以键-元素对,就有可能绑在一块,譬如下面的键1和键15的哈希值是一样的,它们绑在一块的是一个连续的内存区域
    哈希值1----> 键1元素1键15元素15
    哈希值2-----> 键a元素a
    哈希值3-----> 键b元素b
    2019-05-07
    1
  • colonel
    哈希桶怎么存储的,是数组吗?碰撞之后的键值又是怎么存储的,形成链表吗?删除机制中,是删除链表中节点吗?
    建议,不要大篇幅讨论存储性能,可以对内部数据存储,插入,删除,读取机制更多分享

    作者回复: 这些文章里都有写。

    2018-09-22
    1
收起评论
34
返回
顶部