go map
来源:互联网 发布:网络请求失败怎么解决 编辑:程序博客网 时间:2024/04/29 11:16
哈希表是计算机里面比较重要的一个数据结构, 虽然各个哈希表的实现不同, 但是基本上不同的实现都提供快速的增、删、改、查功能。 go语言用map来提供哈希表的功能。
声明和初始化
go语言的map有如下的形式: map[KeyType]ValueType
其中, 要求KeyType必须是 可以比较的( 可以使用==和!=号), ValueType可以是任何类型, 当然这个任意类型也包括另外一个map,比如 map[string]map[string]int 是一个合法的map。
如果有如下语句: var m map[string]int , 那么变量m就是一个key为string类型, value为int类型的map,因为map是引用类型(和go里面的slice一样), 所以m的值是nil,它没有指向任何内部的map数据结构,你可以读这个m,但是写它是会panic掉的,所以你最好不要这样来声明m, 你可以用go自带的make函数: m = make(map[string]int) 来初始化m,这个时候m不是nil了, 你既可以读又可以写这个m了。 当然你也可以用 m = map[string]int{} 来初始化一个空的map, 这个和上面的make函数效果是一样的, 但是我个人还是推荐用make函数来初始化map。
使用map
go为你提供了你熟悉的方式来操作map, 比如 m[“route”] = 66 把key”route”赋了值66。 读取这个key的值并赋给一个新的变量可以用: i := m[“route”] 语句。 如果key不存在, 那么得到的值将是ValueType的 零值, 比如 z := m[“not-exist”] ,因为m的值类型是int型的, 所以z的值会为int类型的零值, 也就是0.
我们可以通过内置函数len来得到map里面元素的个数, 比如len(m)会返回m里面有多少个元素。 如果你想删除map里面的一个特定的key, 你可以用go内置的delete函数, 比如delete(m, “to-be-deleted”) 来删除可以为 “to-be-deleted”的元素。 delete函数没有任何返回值, 如果要删除的key不存在, 那么就不会对这个map做任何修改。
刚才我们说过, 如果取值的时候, key不存在, 那么将返回值类型的零值, 那么假如有如下语句: a := m[“mykey”] , 通过这个语句, a的值为0, 那么我们怎么知道是mykey不存在, 还是说mykey存在并且对应的value值为0呢? go为我们提供了一种方式来区分这个情况, a, ok := m[“mykey”] ,如果mykey不存在,则ok为false, 如果mykey存在, 则ok为true。 当然如果只想确认一个key是否存在, 而不关心这个key对应的value的话, 那么用_, ok = m[“mykey”], 然后判断ok是否为true就行了。
如果你想要遍历map里面的每一个元素, 你可以试用range语句:
for key, value := range m { fmt.Println("Key:", key, "Value:", value)}
也可以在声明的时候直接把一些元素初始化出来:
commits := map[string]int{ "rsc": 3711, "r": 2138, "gri": 1908, "adg": 912,}
利用零值
我们可以利用“map在key不存在的情况下返回value的零值”这个特性, 做一些事情。 比如我们可以用map来模拟集合,valueType为布尔类型(布尔类型的零值是false)。下面的例子遍历Node类型的链表,并且打印遍历的值,它利用一个Node指针的map来避免出现环形链表(这样遍历永远不会结束)。
type Node struct { Next *Node Value interface{} } var first *Node visited := make(map[*Node]bool) for n := first; n != nil; n = n.Next { if visited[n] { fmt.Println("cycle detected") break } visited[n] = true fmt.Println(n.Value) }
表达式visisted[n]的值为true如果n被访问过, 为false如果n没有被访问过。 我们不用那种_, ok = visited[n], 然后判断ok的方式来判断n是否出现过, 利用布尔类型的零值为false可以像上面的代码一样简洁的把这件事情干了。
另外一个利用零值的例子是一个slice的map, append一个nil的slice会自动分配一个新的slice,所以没有必要先判断slice是否为空再append。在下面的例子中,people是一个slice,里面放着结构为Person的数据, Person有Name属性和Likes属性, Likes是这个人的喜好的slice。
type Person struct { Name string Likes []string } var people []*Person likes := make(map[string][]*Person) for _, p := range people { for _, l := range p.Likes { likes[l] = append(likes[l], p) // no need to check if like[l] is nil } }
这样打印喜欢cheese的人的列表:
for _, p := range likes["cheese"] { fmt.Println(p.Name, "likes cheese.") }
打印有多少人喜欢bacon:
fmt.Println(len(likes["bacon"]), "people like bacon.")
key的类型
上面我们说过, map的key必须是 可比较 的(其实自己实现过hash表的人也应该知道, 我们一般对key做hash,然后把hash相同的key放到一个桶上, 有冲突的时候可以挂链,在遍历这个链的时候需要比较key才能找到这个key), 语言规范 对可以比较做了准确的描述,用一个简短的话来说, 布尔型, 数值,字符串,指针,channel和interface都是可以比较的,只包含以上类型的数组或者struct也是可比较的。从上面可以看到, slices,map,函数,都是不可比较的, 所以不能用作map的key(但是可以作为map的value)。 很明显数值类型,字符串类型和其他基本类型可以用来做key,但是struct做key不是特别明显,我们就用一个列子来说明有些时候用struct做key可以使代码更简单。下面的结构(map的map)可以用来按国家统计一个url被访问的次数。
hits := make(map[string]map[string]int)
这个是一个字符串到(字符串到int的map)的map,外层的key表示一个url, 里层的map的key表示国家。下面的代码用来取澳大利亚访问/doc的次数:
n := hits["/doc/"]["au"]
当我们更新一个国家的访问次数的时候, 会很麻烦, 对于每一个外层的key,我们都得判断里层的map是否存在, 如果不存在, 我们要新建一个map:
func add(m map[string]map[string]int, path, country string) { mm, ok := m[path] if !ok { mm = make(map[string]int) m[path] = mm } mm[country]++}add(hits, "/doc/", "au")
如果我们用一个struct做key的话, 就不用这么麻烦的判断内层的map是否为空了:
type Key struct { Path, Country string}hits := make(map[Key]int)hits[Key{"/doc", "au"}]++
取澳大利亚访问/doc的次数也很简单:
n := hits[Key{"/doc", "au"}]
map不是并发访问安全的 ,同时读写同一个map的行为是未定义的,所以你要在多个goroutine里面操作同一个map,需要同步原语来保护的, 比较常见的方法是用 sync.RWMutex . 。 下面的代码定义了一个counter,这个counter是一个匿名struct类型的,这个匿名struct里面就包含了一个map和 嵌入 的sync.RWMutex . 。
var counter = struct{ sync.RWMutex m map[string]int}{m: make(map[string]int)}
在读的时候, 需要加读锁:
counter.RLock()n := counter.m["some_key"]counter.RUnlock()fmt.Println("some_key:", n)
在写的时候, 需要加写锁:
counter.Lock()counter.m["some_key"]++counter.Unlock()
遍历顺序
map遍历的时候,返回的key顺序是 随机 的,而且不保证两次遍历的顺序是相同的, 不要依赖于这个顺序。
本文主要参考的是 https://blog.golang.org/go-maps-in-action, 如有说的不对的地方, 欢迎指正。
- [Go] --- map
- go-map
- go map
- go--map
- 【Go】map
- go语言学习---map
- 【GO语言】MAP
- go语言MAP用法
- Go基础-map
- Go 语言Map(集合)
- go map深度拷贝
- go map slice积累
- GO语言map
- Go语言Map(集合)
- [go语言]slice和map
- Go语言_array,slice,map
- go 速学 - 08 - Map
- Go指南中的练习:map
- spring3 mvc获取请求参数的方法
- 十六进制颜色
- cocos2dx 3.12 lua环境配置与代码调试_02
- React-Native Android物理返回键
- 从达标到卓越 —— API 设计之道
- go map
- java基础_day0011_循环_for_嵌套
- 关于Spring boot
- tomcat启动标题修改,指定jdk
- Laravel框架实现定时Task Scheduling
- JNDI 是什么
- 仿猪来了转盘button的实现
- 浏览器点击返回时,上个页面混乱
- 保持iOS设备屏幕常亮的方法