温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

golang 数据三 (字典)

发布时间:2020-03-05 04:30:07 来源:网络 阅读:2114 作者:100018 栏目:开发技术

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

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

{     m := make(map[string]int)    //创建一个字典     m["a"] = 1      m["b"] = 2      m2 := map[int]struct{   //匿名结构体         x int      }{           1: {x:100},         2: {x:200},     }        fmt.Println(m,m2) }


字典的基本操作

比如:

{     m := make(map[string]int)     m["route"] = 66 //添加key     i := m["route"] //读取key     j := m["root"]  //the value type is int, so the zero value is 0     n := 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        //cannot assign to struct field m[1].age in map }

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

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

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

{     m := map[int] user {         1 : {             name:"tom",             age:19},     }     u := m[1]     u.age = 1     m[1] = u } {     m := map[int] *user {         1 : {             name:"tom",             age:19},     }     m[1].age += 1 }

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

{     var m map[string]int     //p := m["a"]       //ok     m["a"] = 1          //panic: assignment to entry in nil map }

map遍历:

{ //  var m = make(map[string]int)     var m = map[string]int{}     m["route"] = 66     m["root"] = 67     for key,value := range m{         fmt.Println("Key:", key, "Value:", value)     } }

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

{     m := make(map[int]int)     var keys []int     for i := 0 ;i <= 5;i++{         m[i] = i     }                                                                                                                                                            for k, v := range m{         fmt.Println("Key:",k,"Value:",v)     }        for k := range m{         keys = append(keys, k)     }     sort.Ints(keys)     for _, k := range keys{         fmt.Println("Key:",k,"Value:",m[k])     } }

并发

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

    m := make(map[string]int)     go func(){         for {             m["a"] += 1             time.Sleep(time.Microsecond)         }        }()      go func (){          for{             _ = m["b"]             time.Sleep(time.Microsecond)         }        }()      select{} } 输出: fatal error: concurrent map read and map write

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

# go run -race file.go

安全

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

{     var lock = new(sync.RWMutex)                                                                                                                           m := make(map[string]int)     go func(){         for {             lock.Lock()             m["a"]++             lock.Unlock()             time.Sleep(time.Microsecond)         }        }()      go func (){          for{             lock.RLock()             _ = m["b"]             lock.RUnlock()             time.Sleep(time.Microsecond)         }        }()      select{} }

性能

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

package main import "testing" import "fmt" func test() map[int]int {	m := make(map[int]int)	for i:=0; i < 1000; i++{	m[i] = 1	}	return m } func testCap() map[int]int{	m := make(map[int]int,1000)	for i:=0; i < 1000; i++{	m[i] = 1	}	return m } func BenchmarkTest(t *testing.B){	for i:= 0;i < t.N; i++{	test()	} } func BenchmarkTestCap(t *testing.B){	for i:= 0;i < t.N; i++{	testCap()	} } func main(){	resTest := testing.Benchmark(BenchmarkTest)	fmt.Printf("BenchmarkTest \t %d, %d ns/op,%d allocs/op, %d B/op\n", resTest.N, resTest.NsPerOp(), resTest.AllocsPerOp(), resTest.AllocedBytesPerOp())	resTest = testing.Benchmark(BenchmarkTestCap)	fmt.Printf("BenchmarkTestCap \t %d, %d ns/op,%d allocs/op, %d B/op\n", resTest.N, resTest.NsPerOp(), resTest.AllocsPerOp(), resTest.AllocedBytesPerOp()) } 输出: # go run conmap.go BenchmarkTest 	 10000, 160203 ns/op,98 allocs/op, 89556 B/op BenchmarkTestCap 20000, 65478 ns/op,12 allocs/op, 41825 B/op


借鉴:<<雨痕笔记>>

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI