Go语言中map字典类型时散列表(hash table)的实现,因为Go语言中将map中的每个键都看作与其对应元素的索引,所以同一个map中的键都是唯一的.
映射map功能强大之处在于能够基于键快速检索数据
map类型可以写成map[K]V ,其中所有的key必须是相同类型,所有的value也是相同类型,但是K也V可以是不同类型
var 变量名 map[key-type]value-type
变量名 : 就是定义一个map类型的变量名称key-type : map类型数据键的类型 ,通常为 int ,string类型key-value : map类型数据值得类型,可以是int ,string ,bool,map,struct等等map 的声明不会分配内存,初始化需要make,分配内存之后才能使用
package main import "fmt" func main(){ // 声明一个键为int型,值为string型的map var a map[int]string // 声明一个键为int型,值为float型的map var b map[int]float64 // 声明一个键为string型,值为bool型的map var c map[string]bool // 声明一个键为string型,值为map[int]string型的map var d map[string]map[int]string fmt.Println(a) fmt.Println(b) fmt.Println(c) fmt.Println(d) } $ go run main.go map[] map[] map[] map[]初始化方式1
package main import "fmt" func main(){ var a map[int]string a = make(map[int]string,5) a[1] = "a" a[2] = "b" a[5] = "e" a[3] = "c" a[6] = "f" fmt.Println("a = ",a) } $ go run main.go a = map[1:a 2:b 3:c 5:e 6:f]初始化方式2
package main import "fmt" func main(){ // 这种写法显得很冗余 var b map[string]string = map[string]string{ "A" :"left", "D" :"right", "W":"forward", } // 简化写法 fmt.Println("b = ",b) var b2 = map[string]string{ "A" :"left", "D" :"right", "W":"forward", } fmt.Println("b2 = ",b2) } $ go run main.go b = map[A:left D:right W:forward] b2 = map[A:left D:right W:forward]初始化方式3
package main import "fmt" func main(){ c := map[int]string{ 1:"状元", 2:"榜眼", 3:"探花", } fmt.Println(c) } $ go run main.go map[1:状元 2:榜眼 3:探花]初始化方式4
package main import ( "fmt" ) func main(){ e := make(map[string]string) e["beijing"] = "北京" e["shanghai"] = "上海" fmt.Println(e) } $ go run main.go map[beijing:北京 shanghai:上海]map类型数据的增加和修改 ,仅当map中的key不存的时候是新增一个元素,当key存在再给它赋值那就是修改一个key的值了
package main import "fmt" func main() { var m = make(map[int]string) // 给一个map 新增成员 m[1] = "America" m[2] = "China" m[3] = "Russia" fmt.Println(m) // 修改map的数据 m[1] ,m[2] = m[2],m[1] m[3] = "Germany" fmt.Println(m) } $ go run main.go map[1:America 2:China 3:Russia] map[1:China 2:America 3:Germany]使用Go语言内建函数delete()删除map中的一组键值
对map执行删除元素操作时安全的,即使元素不存在,delete删除一个不存在元素返回率 0
package main import "fmt" func main() { var m = make(map[int]string) // 给一个map 新增元素 m[1] = "America" m[2] = "China" m[3] = "Russia" fmt.Println(m) //删除map中的一组键值 delete(m,1) fmt.Println(m) } $ go run main.go map[1:America 2:China 3:Russia] map[2:China 3:Russia]清空map中所有元素,在Go语言中没有直接提供这样的方法和函数,那么清空一个map的唯一途径是重新make一个新的map,让原来的数据块成为"垃圾",由系统的GC执行垃圾回收
package main import "fmt" func main() { m := map[int]string{ 1:"北京", 2:"上海", 3:"广州", 4:"深圳", } fmt.Println(m) // 清空一个map m = make(map[int]string) fmt.Println(m) } $ go run main.go map[1:北京 2:上海 3:广州 4:深圳] map[]在Go语言中map的查找设计的很简洁明确
使用下标来访问map中的元素的元素输出两个值,第一个是访问元素的值,一个是报告元素是否存在的bool值,我们一般将这个bool值命名为 ok
package main import ( "fmt" ) func main(){ var m = map[string]string{ "name":"tom", "age":"99", "addr":"beijing", "work":"programer", } //判断是否找到特定的键,只需要看第二个参数返回值是true或者false就可以 //这里的ok是一个变量,用来接收是否找到指定的键的标识 name ,ok := m["name"] fmt.Println(ok) //true if ok{ fmt.Println(name) // tom } //上面访问map元素的写法,可以合并成一条语句 if addr,ok := m["addr"]; ok{ fmt.Println(addr) } nickname,yes:= m["nickname"] fmt.Println(yes) // false fmt.Println(nickname) } $ go run main.go true tom beijing falsemap的遍历通过 for - range 结构来完成
映射map是无序的集合,所以没有办法预测键值对返回的顺序,故而每次迭代map的顺序也可能不一样.
map和slice 一样是不能进行比较的,唯一能做的比较是和 nil 比较.
要判断两个map是否拥有相同的键和值,必须写循环判断
示例代码只展示一个思路
package main import "fmt" func mapCompare(x, y map[string]string) bool { // 比较长度 if len(x) != len(y) { return false } // 比较键值 for k, xv := range x { if yv, ok := y[k]; !ok || yv != xv { return false } } return true } func main() { var xm = map[string]string{ "name": "tom", "addr": "beijing", } var ym = map[string]string{ "name": "tom", "addr": "beijing", } // map 类型只能和nil 直接比较 if xm == nil || ym == nil { fmt.Println("map xm or ym is nil") } else { fmt.Println("map xm and ym is ok") } // 比较两个map是否拥有相同的键值 if ok := mapCompare(xm,ym) ;ok{ fmt.Println("xm 和 ym 有相同的键和值") } } $ go run main.go map xm and ym is ok xm 和 ym 有相同的键和值并发对一个map进行读写操作会引发竞态问题
package main func main() { var m = make(map[int]int) // 并发函数不断对map进行读写的时候,发生竞态问题 go func() { for { m[1] = 1 } }() go func() { for { _ = m[1] } }() for { } } $ go run main.go fatal error: concurrent map read and map write面临这样的问题,可以采取的一个方案是使用 sync.Map 这是sync包的一个特殊结构,与map很不同,相比较map 是损失了性能,解决的并发读写问题,如果没有并发读写问题,直接使用map即可
package main import ( "fmt" "sync" "time" ) func main() { // 定义一个sync.Map类型 var sm sync.Map // 给 sm 添加元素 sm.Store("name", "zhangshan") sm.Store("addr", "bejing") sm.Store("job", "programmer") //获取一个元素 name, ok := sm.Load("name") if ok { fmt.Println(name) } // 删除一个元素(安全删除) sm.Delete("nameNone") // 遍历sm中的元素 sm.Range(func(key, value interface{}) bool { fmt.Println(key,"===>",value) return true }) // 测试并发读写使用 go func() { for { t := time.Now().Unix() sm.Store("name", t) } }() go func() { for { name, _ := sm.Load("name") fmt.Println(name) } }() for { } }