map的操做和约束

Go语言的字典类型其实就是一个哈希表的特定实现。字典的键的类型是受限的,元素的类型能够是任意类型。算法

字典的键为何受限呢?

典的键-元素对的增删改查的操做,就是哈希表的映射过程。数组

以查找为例:安全

  1. 在哈希表中查找与某个键值对应的元素值的时候,须要先把键值做为参数传给这个哈希表。哈希表用哈希函数把键值转换为哈希值。

哈希值一般是一个无符号的整数。一个哈希表会持有必定数量的哈希桶。并发

  1. 用这个键的哈希值的低几位去定位到一个哈希桶,而后再去这个哈希桶中查找这个键。

因为键-元素对老是被捆绑在一块儿存储的,因此一旦找到了键,就必定能找到对应的元素值。函数

  1. 应的元素值以后,就会将元素值做为结果返回。

咱们已经知道了,映射的第一步是把键值转换成哈希值。那么字典的键不能死哪些类型呢?

回答:字典的键不能是函数类型,字典类型,切片类型。性能

那么,剩下的:基本数据类型,(int系列,float系列,string,复数),数组,结构体,指针,接口等均可以做为键类型。ui

问题解析

Go语言规范规定:在键类型的值之间必须能够施加操做符==!=。也就是说键类型的值必需要支持判等操做。指针

注意:

  1. 键类型是接口类型时,键值的实际类型也不能是上述三种类型。不然会在程序运行时引起panic。
  2. 最好不要把字典的键类型设定为任何接口类型。
  3. 若是键类型是数组类型,要确保该类型的元素类型不是函数类型,字典类型或切片类型
  4. 若是键类型是结构体类型,也要保证其中字段的类型的合法性。不合法的类型被埋藏的多深,都会被Go语言编译器揪出来的。

引伸:优先考虑哪些类型做为字典的键类型?

从性能角度

求哈希和判等的速度越快,对应的类型就越适合做为键类型。code

宽度越小的类型求哈希的速度一般越快。好比布尔类型,整数类型,浮点数类型,复数类型和指针类型。接口

对于字符串类型,因为它的宽度是不定的,因此要看它的值的具体长度,长度越短,求哈希越快

类型的宽度是指它的单个值须要占用的字节数。好比,bool,int8,uint8类型的一个值须要占用的字节都是1个。所以宽度就都是1。

对于高级类型:

数组:对数组求哈希其实是依次求得它的每一个元素的哈希值并进行合并,因此速度就取决于它的元素类型以及它的长度。

结构体:对结构体类型的值求哈希值实际上就是对它的全部字段求哈希值并进行合并。因此关键在于它的各个字段的类型和字段的数量。

接口:对于接口类型,具体的哈希算法由值的实际类型决定。

结论

不建议使用高级数据类型做为字典的键类型。优先选用数值类型和指针类型。一般状况下类型的宽度越小越好。若是非要选择字符串类型的话,最好对键值的长度进行额外的约束。

一个是由于对他们求哈希值和判等的速度较慢,还由于他们的值中存在变数。好比,改变数组中任意一个元素,数组的哈希值就变了。

虽然结构体能够经过控制其中字段的访问权限,来防止外界修改它。

把接口类型做为键类型最危险。

在值为nil的字典上执行读操做会成功吗?那写操做呢?

因为字典是引用类型,当咱们仅仅声明而不初始化一个字典类型的变量的时候,这个变量的值就是nil。

对这个值为nil的字典进行添加键-元素对,会引起错误。其余操做没有问题。

字典类型的值是并发安全的吗?

非原子操做须要加锁,map操做不是并发安全的,map并发读写须要加锁。

判断一个操做是不是原子的,可使用go run race命令作数据的竞争检测。经过 sync.Map 或本身使用sync.RWMutex本身实现并发互斥逻辑

相关文章
相关标签/搜索