字典(Map):map[K]T

K:为键类型,T:为元素(值)类型。
例:map[int] string 一个键类型为int,值类型为string的字典类型

Go语言的字典类型(map)实际上是一个哈希表(hash table)的特定实现,但是字典类型的数据类型是受限的,而哈希表是可以任意类型。
键和元素的这种对应关系叫做“映射”,键就是一个索引,我们通过在哈希表中查找键从而获得与它对应的那个元素。

哈希表的查找过程:

首先需要先把键名作为参数,传给哈希表,哈希表会用哈希函数(hash function)把键值转换为哈希值。

哈希值是一个无符号的整数,哈希表先用这个键的哈希值的低几位去定位到一个哈希桶(bucket,均匀地存储所属哈希表中的键-元素对),

然后再去这个哈希桶中查找这个键,而一旦找到了这个键,就能找到对应的元素值。

随后,哈希表就会把元素值作为结果返回。

综上所述,映射过程的第一步就是把键值转换为哈希值。在Go语言的字典类型中,每个键值都是由它的哈希值代表的,字典不会存储任何键值,只会存储它们的哈希值。

Map的赋值方式:
1. 定义map,赋值给map1:

  1. Var map1 map[int]string
  2. map1 := map[int]string{:"a", :"b", :"c"}

对于字典值来说,如果指定键没有对应的值则默认为该类型的空值。所以map1[5]会返回一个""。

但是这样的话我们就不知道map1[5]到底是""还是map1[5]没有这个值。所以go提供了另外一种写法:

  1. e, ok := map1[]

针对字典的索引表达式可以有两个求职结果,第二个求职结果是bool类型的。它用于表明字典值中是否存在指定的键值对。

2. 用make进行初始化赋值

  1. map2 := make(map[int]string)
  2. map2[] = abc
  3. fmt.Println(map2)

map的使用过程中需要注意以下几点:

1、map是无序的,每次打印出来的map都不会一样,它不能够通过index获取,而必须通过key获取;
2、map的长度不是固定的。和slice类似,map也是一种引用类型;
3、内置的len函数同样适用于map,返回map拥有的key的数量;
4、map的值可以很方便的修改。

问题:字典的键类型不能是哪些类型?

回答:不可以是函数类型、字典类型和切片类型。
分析:Go语言规范中规定,在键类型的值之间必须可以施加操作符“==”和“!=”,必须要支持判等操作。
由于函数类型、字典类型和切片类型不支持判等操作,所以不能是这些类型。

另外,如果键的类型是接口类型(map[interface{}]),那么键值的实际类型也不能是上述三种类型,否则会引发panic。

  1. package main
  2.  
  3. func main() {
  4. // 示例1。
  5. var badMap1 = map[[]int]int{} // 这里会引发编译错误。
  6. _ = badMap1
  7.  
  8. // 示例2。
  9. var badMap2 = map[interface{}]int{
  10. "": ,
  11. []int{}: , // 这里会引发panic。
  12. : ,
  13. }
  14. _ = badMap2
  15.  
  16. // 示例3。
  17. var badMap3 map[[][]string]int // 这里会引发编译错误。
  18. _ = badMap3
  19.  
  20. // 示例4。
  21. type BadKey1 struct {
  22. slice []string
  23. }
  24. var badMap4 map[BadKey1]int // 这里会引发编译错误。
  25. _ = badMap4
  26.  
  27. // 示例5。
  28. var badMap5 map[[][][][]string]int // 这里会引发编译错误。
  29. _ = badMap5
  30.  
  31. // 示例6。
  32. type BadKey2Field1 struct {
  33. slice []string
  34. }
  35. type BadKey2 struct {
  36. field BadKey2Field1
  37. }
  38. var badMap6 map[BadKey2]int // 这里会引发编译错误。
  39. _ = badMap6
  40.  
  41. }

该例中,用字面量声明了一个字典badMap2,并进行了初始化,包含了三个键-元素对,其中第二个键元素对的键值是[]int{2},元素值是2,从语法上来讲,这样是可以的。

这里的badMap2的类型是接口类型interface{},元素值类型为int,这样的声明不会引起什么错误,Go语言编译器也不会报错。

但是,在运行的时候,Go语言的运行时系统(runtime库)就会抛出panic错误,所以最好不要把字典类的键类型设定为任何接口类型。

如果键的类型是数组类型,那么还要确保该类型的元素类型不是函数类型、字典类型或切片类型。

比如,由于类型[1][]string的元素类型是[string],所以它也不能作为字典类型的键类型。

另外,如果键类型是结构体类型(struct),那么还要保证其中字段的类型的合法性。

为什么键类型必须支持判等操作?

因为只有哈希值和键值都相等,Go才能通过键值找到元素值。

扩展:

Q1: 应该优先考虑哪些类型作为字典的键类型?

从性能的角度讲,在“映射”的过程中,“把键值转换为哈希值”和“把要查找的键值和bucket哈希桶中的键值做比对”是两个比较耗时的操作,

因此,求哈希和判等操作的速度越快,对于的类型就越适合作为键类型。

求哈希为例,宽度越小的类型速度越快,如bool,整数类型、浮点数类型、复数类型和指针类型。
对于字符串类型,由于它的宽度是不定的,所以要看它的值的具体长度,长度越短求哈希越快。
类型的宽度是指它的单个值需要占用的字节数,如bool、int8和uint8类型的值需要占用的字节数都是1,宽度就是1.
高级类型中,数组类型的值求哈希实际上是依次求得它每个元素的哈希值并键合并,所以速度就取决于元素类型以及长度。
结构体类型的值求哈希,实际上是对它的所有字段求哈希并进行合并,所以关键在于它的各个字段的类型以及字段的数量。
对于接口类型,具体的哈希算法由值的实际类型决定。

不建议使用高级类型作为字典的键类型,因为判等速度慢,而且在他们的值中存在变数。
比如,对于一个数组,我们可以任意改变元素值,在变化前后,它代表了两个不同的键值。
把接口类型作为字段的键类型最危险,会导致panic错误,甚至程序崩溃。

Q2: 在值为nil的字典上执行读操作会成功吗?写操作呢?

demo19.go

  1. package main
  2.  
  3. import "fmt"
  4.  
  5. func main() {
  6. var m map[string]int
  7.  
  8. key := "two"
  9. elem, ok := m["two"]
  10. fmt.Printf("The element paired with key %q in nil map: %d (%v)\n",
  11. key, elem, ok)
  12.  
  13. fmt.Printf("The length of nil map: %d\n", len(m))
  14.  
  15. fmt.Printf("Delete the key-element pair by key %q...\n", key)
  16. delete(m, key)
  17.  
  18. elem =
  19. fmt.Println("Add a key-element pair to a nil map...")
  20. //m["two"] = elem // 这里会引发panic。
  21. }

字典是引用类型,所以当我们仅声明而不初始化一个字典类型的变量的时候,它的值会是nil。
除了添加键元素对,我们在一个值为nil的字典上做任何操作,都不会引起错误。
而如果我们要在这个值为nil的字典中添加键元素对时,Go的runtime系统会抛出一个panic。

思考题:字典类型的值是并发安全的吗?如果不是,那么我们只在字典上添加或删除键元素对的情况下,依然不安全吗?

A:go实现的map不是多协程安全的(并发访问可能导致):
fatal error: concurrent map read and map write
对字典操作最好用1.9之后新加入的sync.Map, 如果不用的话需要对goroutine加读写锁。
非原子操作需要加锁,map并发读写需要加锁,判断一个操作是否是原子的可以使用go run race命令做数据的竞争检测。

[Golang学习笔记] 09 字典的更多相关文章

  1. golang学习笔记5 用bee工具创建项目 bee工具简介

    golang学习笔记5 用bee工具创建项目 bee工具简介 Bee 工具的使用 - beego: 简约 & 强大并存的 Go 应用框架https://beego.me/docs/instal ...

  2. golang学习笔记20 一道考察对并发多协程操作一个共享变量的面试题

    golang学习笔记20 一道考察对并发多协程操作一个共享变量的面试题 下面这个程序运行的能num结果是什么? package main import ( "fmt" " ...

  3. golang学习笔记19 用Golang实现以太坊代币转账

    golang学习笔记19 用Golang实现以太坊代币转账 在以太坊区块链中,我们称代币为Token,是以太坊区块链中每个人都可以任意发行的数字资产.并且它必须是遵循erc20标准的,至于erc20标 ...

  4. golang学习笔记18 用go语言编写移动端sdk和app开发gomobile

    golang学习笔记18 用go语言编写移动端sdk和app开发gomobile gomobile的使用-用go语言编写移动端sdk和app开发https://blog.csdn.net/u01249 ...

  5. golang学习笔记17 爬虫技术路线图,python,java,nodejs,go语言,scrapy主流框架介绍

    golang学习笔记17 爬虫技术路线图,python,java,nodejs,go语言,scrapy主流框架介绍 go语言爬虫框架:gocolly/colly,goquery,colly,chrom ...

  6. golang学习笔记16 beego orm 数据库操作

    golang学习笔记16 beego orm 数据库操作 beego ORM 是一个强大的 Go 语言 ORM 框架.她的灵感主要来自 Django ORM 和 SQLAlchemy. 目前该框架仍处 ...

  7. golang学习笔记14 golang substring 截取字符串

    golang学习笔记14 golang substring 截取字符串golang 没有java那样的substring函数,但支持直接根据 index 截取字符串mystr := "hel ...

  8. golang学习笔记13 Golang 类型转换整理 go语言string、int、int64、float64、complex 互相转换

    golang学习笔记13 Golang 类型转换整理 go语言string.int.int64.float64.complex 互相转换 #string到intint,err:=strconv.Ato ...

  9. golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题

    golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题 今天测试了重新建一个项目生成新的表,然后复制到旧的项目 ...

随机推荐

  1. ubuntu13.04更新源

    最近163的源出问题了,又要换一次源. 报错如下: 查更新源的命令查了好多次,这次还是记下来吧,估计以后还会用到很多次. 常规来说,是要先备份的,不过感觉备份也没什么用,所以就直接跳过吧.. 1  打 ...

  2. webpack笔记一 起步

    webpack笔记一 起步 安装 对于大多数项目,我们建议本地安装(--save-dev).这可以在引入突破式变更(breaking change)版本时,更容易分别升级项目. 起步 初始化项目 mk ...

  3. 用AutoHotkey实现Excel从表B提取匹配数据到表A

    说明:为表述方便,待填的表为[表A],资料库的表称为[表B].该工具可以快捷地从[表B]中提取相关数据到[表A],顺序和列可自定义. 使用方法:1.打开[ExcelGetFromB.exe](如要打开 ...

  4. scala当中的类型参数

    类型参数主要就是研究scala当中的类或者scala当中的方法的泛型 1.scala当中的类的泛型         object Demo8 {          def main(args: Arr ...

  5. CentOS7.X安装Redis-4.0.8以及Redis集群搭建

    安装redis 安装前的准备 yum install \ vim \ wget \ make \ gcc \ gcc-c++ \ automake \ autoconf \ -y \ 下载解压并安装 ...

  6. 异步模式:Callbacks, Promises & Async/Await

    [译]异步JavaScript的演变史:从回调到Promises再到Async/Await https://www.i-programmer.info/programming/theory/8864- ...

  7. Sublime Text 3中文乱码问题解决

    Sublime Text 3是我MacBook Pro最喜欢的代码编辑器,没有之一,因为她的性感高亮代码配色,更因为它的小巧,但是它默认不支持GBK的编码格式,因此打开GBK的代码文件,如果里面有中文 ...

  8. [Python 多线程] Condition (十)

    Condition常用于生产者.消费者模型,为了解决生产者消费者速度匹配问题. 构造方法Condition(lock=None),可以传入一个Lock或RLock对象,默认RLock. 方法: acq ...

  9. 让IIS 7 如同IIS 8 第一次请求不变慢(转载)

    当我们把网站部署在IIS7或IIS6S的时候,每当IIS或是Application Pool重启后,或者站点在长时间没有请求访问后,第一次请求网站反应总是很慢.所以每次网站更新都会给第一个用户带来不好 ...

  10. Python 学习笔记(十四)Python类(一)

    基本概念 问题空间:问题空间是问题解决者对一个问题所达到的全部认识状态,它是由问题解决者利用问题所包含的信息和已贮存的信息主动的地构成的. 初始状态:一开始时的不完全的信息或令人不满意的状况: 目标状 ...