golang 数据三 (字典)

前端之家收集整理的这篇文章主要介绍了golang 数据三 (字典)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

golang基本数据结构Map也叫字典,字典的声明格式如下: map[KeyType]ValueType

字典是无序键值对集合,字典要求KeyType必须是支持相等运算符(==,!=)的数据类型,比如:数字、字符串、指针、数组、结构体以及对应的接口类型,而ValueType可以是任意类型,字典也是引用类型,使用make函数或者初始化表达式语句来创建。比如:

  1. {
  2. m:=make(map[string]int)//创建一个字典
  3. m["a"]=1
  4. m["b"]=2
  5.  
  6. m2:=map[int]struct{//匿名结构体
  7. xint
  8. }{
  9. 1:{x:100},2:{x:200},}
  10. fmt.Println(m,m2)
  11. }


字典的基本操作

比如:

  1. {
  2. m:=make(map[string]int)
  3. m["route"]=66//添加key
  4. i:=m["route"]//读取key
  5. j:=m["root"]//thevaluetypeisint,sothezerovalueis0
  6. n:=len(m)//获取长度
  7. //n:=cap(m)//???引发一个思考
  8. delete(m,"route")//删除key
  9. }

如果访问不存在的键,不会引发错误,而会默认返回ValueType的零值,那么还能否根据零值来判断key的存在呢?

在读取map操作时,可以使用双返回值来正常读取map,比如:

  1. {
  2. j,ok:=m["root"]
  3. }
  4. 说明:
  5. ok是个bool类型变量,如果key真实存在,则ok的值为true,反之为false,如果你只是想测试key是否存在而不
  6. 获取值的话,可以使用忽略字符"_"

修改map值操作时,因为受内存访问安全和哈希算法等缘故,字典被设计成"not adrressable",因此不能直接修改value成员(结构体或数组)。比如:

  1. {
  2. m:=map[int]user{
  3. 1:{
  4. name:"tom",age:19},}
  5. m[1].age=1//cannotassigntostructfieldm[1].ageinmap
  6. }

但是有两种方式可以实现直接修改map的value成员:

  1. 是对整个value进行重新复制

  2. 声明map时valueType为指针类型

  1. {
  2. m:=map[int]user{
  3. 1:{
  4. name:"tom",}
  5. u:=m[1]
  6. u.age=1
  7. m[1]=u
  8. }
  9. {
  10. m:=map[int]*user{
  11. 1:{
  12. name:"tom",}
  13. m[1].age+=1
  14. }

不能对nil字典进行写操作,但是可以读,比如:

  1. {
  2. varmmap[string]int
  3. //p:=m["a"]//ok
  4. m["a"]=1//panic:assignmenttoentryinnilmap
  5. }

map遍历:

  1. {
  2. //varm=make(map[string]int)
  3. varm=map[string]int{}
  4. m["route"]=66
  5. m["root"]=67
  6. forkey,value:=rangem{
  7. fmt.Println("Key:",key,"Value:",value)
  8. }
  9. }

因为map是无序的,如果想按照有序key输出的话,可以先把所有的key取出,然后对key进行排序,再遍历map,比如:

  1. {
  2. m:=make(map[int]int)
  3. varkeys[]int
  4. fori:=0;i<=5;i++{
  5. m[i]=i
  6. }
  7.  
  8. fork,v:=rangem{
  9. fmt.Println("Key:",k,v)
  10. }
  11. fork:=rangem{
  12. keys=append(keys,k)
  13. }
  14. sort.Ints(keys)
  15. for_,k:=rangekeys{
  16. fmt.Println("Key:",m[k])
  17. }
  18. }

并发

字典不是并发安全的数据结构,如果某个任务正在对字典进行写操作,那么其他任务就不能对该字典执行并发操作(读、写、删除),否则会导致程序崩溃,比如:

  1. m:=make(map[string]int)
  2. gofunc(){
  3. for{
  4. m["a"]+=1
  5. time.Sleep(time.Microsecond)
  6. }
  7. }()
  8.  
  9. gofunc(){
  10. for{
  11. _=m["b"]
  12. time.Sleep(time.Microsecond)
  13. }
  14. }()
  15. select{}
  16. }
  17. 输出
  18. fatalerror:concurrentmapreadandmapwrite

GO语言编译器提供了这种问题(竞争)的检测方式,比如:

  1. #gorun-racefile.go

安全

可以使用 sync.RWMutex 实现同步,避免并发环境多goroutings同时读写操作,继续完善上面的例子,比如:

  1. {
  2. varlock=new(sync.RWMutex)
  3. m:=make(map[string]int)
  4. gofunc(){
  5. for{
  6. lock.Lock()
  7. m["a"]++
  8. lock.Unlock()
  9. time.Sleep(time.Microsecond)
  10. }
  11. }()
  12.  
  13. gofunc(){
  14. for{
  15. lock.RLock()
  16. _=m["b"]
  17. lock.RUnlock()
  18. time.Sleep(time.Microsecond)
  19. }
  20. }()
  21. select{}
  22. }

性能

在创建字典时预先准备足够的空间有助于提升性能,减少扩张时引发内存动态分配和重复哈希操作,比如:

  1. packagemain
  2.  
  3. import"testing"
  4. import"fmt"
  5.  
  6. functest()map[int]int{
  7. m:=make(map[int]int)
  8. fori:=0;i<1000;i++{
  9. m[i]=1
  10. }
  11. returnm
  12. }
  13.  
  14. functestCap()map[int]int{
  15. m:=make(map[int]int,1000)
  16. fori:=0;i<1000;i++{
  17. m[i]=1
  18. }
  19. returnm
  20. }
  21.  
  22.  
  23. funcBenchmarkTest(t*testing.B){
  24. fori:=0;i<t.N;i++{
  25. test()
  26. }
  27. }
  28.  
  29. funcBenchmarkTestCap(t*testing.B){
  30. fori:=0;i<t.N;i++{
  31. testCap()
  32. }
  33. }
  34.  
  35. funcmain(){
  36. resTest:=testing.Benchmark(BenchmarkTest)
  37. fmt.Printf("BenchmarkTest\t%d,%dns/op,%dallocs/op,%dB/op\n",resTest.N,resTest.NsPerOp(),resTest.AllocsPerOp(),resTest.AllocedBytesPerOp())
  38. resTest=testing.Benchmark(BenchmarkTestCap)
  39. fmt.Printf("BenchmarkTestCap\t%d,resTest.AllocedBytesPerOp())
  40. }
  41. 输出
  42. #gorunconmap.go
  43. BenchmarkTest 10000,160203ns/op,98allocs/op,89556B/op
  44. BenchmarkTestCap20000,65478ns/op,12allocs/op,41825B/op


借鉴:<<雨痕笔记>>

猜你在找的Go相关文章