golang 数据三 (字典)
golang基本数据结构Map也叫字典,字典的声明格式如下: map[KeyType]ValueType
字典是无序键值对集合,字典要求KeyType必须是支持相等运算符(==,!=)的数据类型,比如:数字、字符串、指针、数组、结构体以及对应的接口类型,而ValueType可以是任意类型,字典也是引用类型,使用make函数或者初始化表达式语句来创建。比如:
{m:=make(map[string]int)//创建一个字典m["a"]=1m["b"]=2m2:=map[int]struct{//匿名结构体xint}{1:{x:100},2:{x:200},}fmt.Println(m,m2)}
字典的基本操作
比如:
{m:=make(map[string]int)m["route"]=66//添加keyi:=m["route"]//读取keyj:=m["root"]//thevaluetypeisint,sothezerovalueis0n:=len(m)//获取长度//n:=cap(m)//???引发一个思考delete(m,"route")//删除key}
如果访问不存在的键,不会引发错误,而会默认返回ValueType的零值,那么还能否根据零值来判断key的存在呢?
在读取map操作时,可以使用双返回值来正常读取map,比如:
{j,ok:=m["root"]}说明:ok是个bool类型变量,如果key真实存在,则ok的值为true,反之为false,如果你只是想测试key是否存在而不获取值的话,可以使用忽略字符"_"。
在修改map值操作时,因为受内存访问安全和哈希算法等缘故,字典被设计成"not adrressable",因此不能直接修改value成员(结构体或数组)。比如:
{m:=map[int]user{1:{name:"tom",age:19},}m[1].age=1//cannotassigntostructfieldm[1].ageinmap}
但是有两种方式可以实现直接修改map的value成员:
是对整个value进行重新复制
声明map时valueType为指针类型
{m:=map[int]user{1:{name:"tom",age:19},}u:=m[1]u.age=1m[1]=u}{m:=map[int]*user{1:{name:"tom",age:19},}m[1].age+=1}
不能对nil字典进行写操作,但是可以读,比如:
{varmmap[string]int//p:=m["a"]//okm["a"]=1//panic:assignmenttoentryinnilmap}
map遍历:
{//varm=make(map[string]int)varm=map[string]int{}m["route"]=66m["root"]=67forkey,value:=rangem{fmt.Println("Key:",key,"Value:",value)}}
因为map是无序的,如果想按照有序key输出的话,可以先把所有的key取出,然后对key进行排序,再遍历map,比如:
{m:=make(map[int]int)varkeys[]intfori:=0;i<=5;i++{m[i]=i}fork,v:=rangem{fmt.Println("Key:",k,"Value:",v)}fork:=rangem{keys=append(keys,k)}sort.Ints(keys)for_,k:=rangekeys{fmt.Println("Key:",k,"Value:",m[k])}}
并发
字典不是并发安全的数据结构,如果某个任务正在对字典进行写操作,那么其他任务就不能对该字典执行并发操作(读、写、删除),否则会导致程序崩溃,比如:
m:=make(map[string]int)gofunc(){for{m["a"]+=1time.Sleep(time.Microsecond)}}()gofunc(){for{_=m["b"]time.Sleep(time.Microsecond)}}()select{}}输出:fatalerror:concurrentmapreadandmapwrite
GO语言编译器提供了这种问题(竞争)的检测方式,比如:
#gorun-racefile.go
安全
可以使用 sync.RWMutex 实现同步,避免并发环境多goroutings同时读写操作,继续完善上面的例子,比如:
{varlock=new(sync.RWMutex)m:=make(map[string]int)gofunc(){for{lock.Lock()m["a"]++lock.Unlock()time.Sleep(time.Microsecond)}}()gofunc(){for{lock.RLock()_=m["b"]lock.RUnlock()time.Sleep(time.Microsecond)}}()select{}}
性能
在创建字典时预先准备足够的空间有助于提升性能,减少扩张时引发内存动态分配和重复哈希操作,比如:
packagemainimport"testing"import"fmt"functest()map[int]int{m:=make(map[int]int)fori:=0;i<1000;i++{m[i]=1}returnm}functestCap()map[int]int{m:=make(map[int]int,1000)fori:=0;i<1000;i++{m[i]=1}returnm}funcBenchmarkTest(t*testing.B){fori:=0;i<t.N;i++{test()}}funcBenchmarkTestCap(t*testing.B){fori:=0;i<t.N;i++{testCap()}}funcmain(){resTest:=testing.Benchmark(BenchmarkTest)fmt.Printf("BenchmarkTest\t%d,%dns/op,%dallocs/op,%dB/op\n",resTest.N,resTest.NsPerOp(),resTest.AllocsPerOp(),resTest.AllocedBytesPerOp())resTest=testing.Benchmark(BenchmarkTestCap)fmt.Printf("BenchmarkTestCap\t%d,%dns/op,%dallocs/op,%dB/op\n",resTest.N,resTest.NsPerOp(),resTest.AllocsPerOp(),resTest.AllocedBytesPerOp())}输出:#gorunconmap.goBenchmarkTest10000,160203ns/op,98allocs/op,89556B/opBenchmarkTestCap20000,65478ns/op,12allocs/op,41825B/op
借鉴:<<雨痕笔记>>
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。