映射是一种数据结构,用于存储一系列无序的键值对,映射基于键来储存值,映射功能强大的地方是,能基于键快速检索数据,键就像索引一样,指向与该键关联的值。
与C++,JAVA不一样,go使用映射map不需要引入任何库。
因为映射也是一个数据集合,所以也可以使用类似处理数组和切片的方式来迭代映射中的元素。但映射是无序集合。所以即使以同样的书序保存键值对,每次迭代映射时,元素顺序也可能不一样,无序的原因是映射的实现使用了散列表。
go语言中的map在底层是用哈希表实现的,在goroot/src/pkg/runtime/hashmap/go可以查看他的实现细节,go语言的map是一个hash数组列表,而不是想C++一样使用红黑树,与传统的hashmap一样,go语言的map由一个bucket组成
列表中的每一个元素都被称为bucket的结构体,每个backet可以保存8个键值对,所以元素将被hash算法跳入到数组的bucket中,bucket填满后,将通过一个overflow指针来扩展一个bucket,从未形成链表,以此解决hash冲突问题,简单来说,map就是一个bucket指针型的一堆数组,每个bucket指针下面则不定长,可能挂着bucket指针list
go语言创建并初始化映射有很多方法,使用内置的make函数或者使用映射字面量都是最常见的办法
//创建一个映射,键的类型是string,值的类型是int
dict :=make(map[string]int)
//创建一个映射,键和值的类型都是string
//使用两个键值对初始化映射
dict := map[string]string{"red": "#da1337","orange":"#e95a22"}
使用映射字面量是更常用的方法,映射的初始长度会根据初始化时指定的键值对的数量来确定。映射的键可以是任何值,这个值的类型并不限制,内置的类型或者结构类型都可以,不过需要确定这个值可以使用==运算符做比较。需要注意的是,切片和函数以及包含切片的结构类型由于具有引用语义,均不能作为映射的键,使用这些类型会造车编译错误
将映射传递给函数
在函数间传递映射并不会制造出改映射的的一个副本。实际上,当传递映射给一个函数,并对函数做了这个修改时,所有对这个映射的引用都会察觉到这个修改
package main
import (
"fmt"
)
func main() {
colors := map[string]string{
"AliceBlue": "#fffff",
"Coral": "rrrd",
"DarkGary": "dhwis",
"ForesrGreen": "djsksa",
}
for key, value := range colors {
fmt.Printf("key:%s value :%s\n", key, value)
}
removeColor(colors, "Coral")
for key, value := range colors {
fmt.Printf("key :%s value :%s \n", key, value)
}
}
func removeColor(colors map[string]string, key string) {
delete(colors, key)
}
如果是数组则不尽然,设计开辟两个内存空间的问题,在函数调用复制并不会影响原数组,所以数组的时候需要调用数组指针。但是映射可以,所以映射在函数间传递时并不会制造出一个改映射的副本。
可以看到在调用了removecolor之后,main函数引用的映射不在有coral颜色了,这个特性和切片类型。go语言里切片经常来处理数据的集合,映射用来处理具有键值对结构的数据
内置函数make可以创建切片和映射,并指定原始的长度和容量,也可以直接使用切片和映射字面量,或者使用字面量作为变量的初始值,切片有容量限制,不过可以使用内置的append函数拓展容量,映射的增长没有容量或者任何限制
内置函数len可以用来获取切片或者映射的长度,内置函数cap只能用于切片,通过组合,可以创建多维数组和多维切片,也可以使用切片或者其他映射作为映射的值。但是切片不能用作映射的键,将切片或者映射传递给函数成本很小,并且不会复制底层的数据结构。