字典(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. JavaScript浏览器对象模型(BOM)之location对象

    一.概述: location 是 BOM 对象之一,它提供了与当前窗口中加载的文档有关的信息,还提供了一些导航功能. 事实上,location 对象是 window 对象的属性,也是 document ...

  2. BZOJ 1303 中位数图 模拟

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1303 题目大意: 给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位 ...

  3. LCTF wp简单复现

    1.T4lk 1s ch34p,sh0w m3 the sh31l 代码如下: <?php $SECRET = `../read_secret`; $SANDBOX = "../dat ...

  4. pwnhub_WTP攻击思路--self-xss高级利用

    1.self-xss+302跳转构造csrf的利用: 1.login.php 存在跳转2.http://54.223.108.205:23333/login.php?redirecturl=//vps ...

  5. Js 中的 this

    Js 中 this 的理解   this 是啥 ? this是 JavaScript 语言的一个关键字,它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用; 随着函数使用场合的不同,this ...

  6. 【bootstrap】.container与.container_fluid

    .container与.container_fluid是bootstrap中的两种不同类型的外层容器,区别是: .container 类用于固定宽度并支持响应式布局的容器.不用你自己设宽度,通过< ...

  7. [Python 多线程] 详解daemon属性值None,False,True的区别 (五)

    本文以多个例子介绍Python多线程中daemon属性值的区别. 回顾: 前面的文章简单介绍了在现代操作系统中,每一个进程都认为自己独占所有的计算机资源. 或者说线程就是独立的王国,进程间是相对独立的 ...

  8. 简单属性margin和padding

    关于margin属性的介绍 margin:20px 上 右 下 左 都是20px margin:20px 40 px 上 下 20px 左 右 40px margin:20px 40px  60px ...

  9. 【SP2713 GSS4 - Can you answer these queries IV】 题解

    题目链接:https://www.luogu.org/problemnew/show/SP2713 真暴力啊. 开方你开就是了,开上6次就都没了. #include <cmath> #in ...

  10. Loadrunner之HTTP脚本编写

    Loadrunner之HTTP脚本编写 刚学习性能测试的时候还是建议以录制脚本为好,从录制的脚本中去了解脚本结构,各个函数的用法,慢慢再去尝试自己去编写脚本,如果想往技术方向发展,建议最好学习一门编程 ...