[TOC]
Map类型又可以称之为字典类型,是一种无序的、基于key:value的数据结构
定义语法如下
| var 变量名 map[Key的类型]Value的类型 |
需要注意的是:
1、map中所有的Key都必须是相同的类型,所有的value也必须都是相同的类型,但是key与value之间可以是不同的类型
2、关于key的类型:Key必须是可以与非nil值进行相等性判断的类型(所有的值类型都支持相等性判断,所有的引用类型只能与nil值判断),因此我们可以通过与key的相等性判断来判定key是否存在。虽然浮点类型也支持与非nil值进行相等性运算,但是不建议用浮点型作为Key,因为浮点数存在非数NaN,而NaN与任何浮点数都不相等,这便失去了意义。
3、关于value的类型:可以是任意类型。
因为map类型也属于复合类型中的引用类型,所以
(1)直接声明一个map类型,零值为nil(引用类型的零值都为nil),nil代表空、没有分配内存。
| var m map[string]int |
| fmt.Println(m) |
| |
| fmt.Println(m==nil) |
| m["k1"]=111 |
(2)初始化切片类型的方式有两种(与切片的初始化都一样)
方式一:先调用make()函开辟空间,然后再塞值
| var m = make(map[string]int) |
| fmt.Println(m) |
| fmt.Println(m==nil) |
| |
| m["k1"]=111 |
| fmt.Println(m) |
方式二:一步到位式,即赋一个完整的值,该方式等同于make完毕之后再塞值,如下所示
| scoreMap:=map[string]int{ |
| "egon":99, |
| "铁蛋":100, |
| "铜蛋":88, |
| } |
| |
| |
| scoreMap := make(map[string]int) |
| scoreMap["egon"] = 99 |
| scoreMap["铁蛋"] = 100 |
| scoreMap["铜蛋"] = 88 |
| |
| fmt.Println(scoreMap["egon"]) |
延伸:我们可以创建一个空的map:表达式是map[string]int{}
| var scoreMap = map[string]int{} |
| fmt.Println(scoreMap == nil) |
| |
| scoreMap["egon"] = 99 |
| fmt.Println(scoreMap["egon"]) |
| var m = make(map[string]string) |
| |
| m["name"]="egon" |
| m["age"]="18" |
| m["gender"]="male" |
| fmt.Println(m) |
| 了解:当新增元素时,map也会随着元素数量的增长而重新分配更大的内存空间,即Go中map实现中元素的地址是变化的, |
| |
| |
| delete(m,"gender") |
| fmt.Println(m) |
| delete(m,"kkk") |
| |
| |
| m["name"]="Egon" |
| fmt.Println(m) |
| |
| |
| fmt.Println(m["name"]) |
| fmt.Println(m["gender"]) |
| |
| |
| var ages = make(map[string]int) |
| ages["egon"] += 1 |
| fmt.Println(ages) |
| |
| 或者 |
| ages["egon"]++ |
| fmt.Println(ages) |
针对操作m[key],当key不存在时,返回的是value对应类型的零值,而极有可能出现key存在,但该key对应的值与其零值是相等的,如下所示
| var ages = make(map[string]int) |
| ages["张三"]=0 |
| fmt.Println(ages["张三"]) |
| fmt.Println(ages["egon"]) |
所以我们无法通过m[key]取到的值作为判断依据来判定key是否存在,需要这么做
| age1,ok:=ages["张三"] |
| fmt.Println(age1,ok) |
| |
| age2,ok:=ages["egon"] |
| fmt.Println(age2,ok) |
| |
| if ok { |
| fmt.Println(age2) |
| } else { |
| fmt.Println("查无此人") |
| } |
(1)map与slice一样,同属于复合类型中的引用类型,复合类型中的引用类型不支持直接进行相等性判断,只能与nil直接进行相等性判断,
若要判断两个map是否包含相同的key和value,我们需要自己循环取出map中的key和value逐一进行比较,如下所示
| func equal(x, y map[string]int) 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 |
| } |
注意注意注意:我们不应该简单地用xv != y[k]判断
| package main |
| |
| import ( |
| "fmt" |
| ) |
| |
| func equal(x, y map[string]int) bool { |
| if len(x) != len(y) { |
| return false |
| } |
| for k, xv := range x { |
| if xv != y[k] { |
| return false |
| } |
| } |
| return true |
| } |
| |
| func main() { |
| res := equal(map[string]int{"A": 0}, map[string]int{"B": 0}) |
| fmt.Println(res) |
| } |
(1)Go语言中使用for range
遍历map
| var ages = make(map[string]int) |
| ages["egon"] = 18 |
| ages["张三"] = 19 |
| ages["李四"] = 20 |
| |
| |
| for k,v := range ages { |
| fmt.Println(k,v) |
| } |
| |
| |
| for k,_ := range ages { |
| fmt.Println(k,v) |
| } |
| |
| for k := range ages { |
| fmt.Println(k) |
| } |
| |
| |
| for _,v := range ages { |
| fmt.Println(v) |
| } |
(2)遍历map时的元素顺序与添加键值对的顺序无关,若想按照指定顺序遍map,需要将map中的key存入切片,然后对切片进行排序后,依据切片中排序好的key依次取map中的value
| var ages = make(map[string]int) |
| ages["egon"] = 18 |
| ages["张三"] = 19 |
| ages["李四"] = 20 |
| ages["王五"] = 21 |
| ages["牛佰"] = 22 |
| |
| |
| for k, v := range ages { |
| fmt.Println(k, v) |
| } |
| |
| |
| var keys []string |
| |
| for k := range ages { |
| keys = append(keys, k) |
| } |
| sort.Strings(keys) |
| |
| for _, k := range keys { |
| fmt.Printf("%s\t%d\n", k, ages[k]) |
| } |
| |
| |
| 因为我们一开始就知道keys的最终大小,因此给切片keys分配一个合适的大小将会更有效,如下所示 |
| keys := make([]string, 0, len(ages)) |
对比同为复合类型中的切片来看,我们可以取切片元素的地址
| numSlice:=[]int{111,222,333} |
| fmt.Println(&numSlice[0]) |
但是我们不能取map中value的地址
| var m = make(map[string]int) |
| m["k1"]=111 |
| fmt.Println(&m["k1"]) |
了解:map中的元素为何不可以被取地址
| 当我们根据key去取map类型中的元素值时。若key不存在则返回零值,而零值是常量,我们无法取常量的地址 |
map在函数参数中的传递与切片一样都是属于“引用传递”。
内置的引用类型,如 slice,map,interface,channel,这些类型比较特殊,声明他们的时候,实际上是创建了一个 header, 对于他们也是直接定义值接收者类型的方法。这样,调用函数时,是直接 copy 了这些类型的 header,而 header 本身就是为复制设计的。
统计一个字符串中单词出现的次数
| var s="Love me, Love my dog" |
| |
| var wordCount=make(map[string]int,10) |
| |
| words:=strings.Split(s," ") |
| |
| for _,word:=range words{ |
| wordCount[word]++ |
| } |
| fmt.Println(wordCount) |