1. 映射
现在有一个需求,我们要统计一个数组中,这些字符串出现了多少次,在没有映射之前,我们的思路可以是创建两个切片类,然后一个用来存储数组中的字符串到不同下标,再用另一个切片来存储这个字符串的出现的次数,这样两个切片相互映射。但是这样有一个缺点就是当数据量很大的时候,显然是比较耗时的,那么我们就可以使用到 Go 里面的 map 集合来存储。
定义:var myMap map[string] int
,可以看到定义的方法 map
是关键字,[string]
是key,int
是value
package main
import "fmt"
func main() {
var myMap map[string]int //声明一个映射
myMap = make(map[string]int) //真正创建一个映射
fmt.Printf("myMap: %v\n", myMap) //myMap: map[]
}
下面短变量声明的方式更加方便:
package main
import "fmt"
func main() {
ranks := make(map[string]int)
fmt.Printf("ranks: %v\n", ranks) //ranks: map[]
}
映射的赋值和取值的语法和数组和切片的赋值和取值有点类似。但是数组和切片仅允许使用整型作为元素索引,而映射可以选择几乎所有的类型来作为主键。
package main
import "fmt"
func main() {
ranks := make(map[string]int)
ranks["gold"] += 1
ranks["silver"] += 1
ranks["bronze"] += 1
fmt.Printf("ranks: %v\n", ranks) //ranks: map[bronze:1 gold:1 silver:1]
}
取值也是和数组的用法一样的
package main
import "fmt"
func main() {
ranks := make(map[string]int)
ranks["gold"] += 1
ranks["silver"] += 1
ranks["bronze"] += 1
fmt.Println(ranks["bronze"]) //1
fmt.Println(ranks["silver"]) //1
fmt.Println(ranks["gold"]) //1
}
2. 映射字面量
创建字面量映射:
package main
import "fmt"
func main() {
myMap := map[string]int{"a":12, "b":13}
fmt.Printf("myMap: %v\n", myMap) //myMap: map[a:12 b:13]
elements := map[string]string{
"H":"Hydrogen",
"Li":"Lithium",
}
fmt.Printf("elements: %v\n", elements) //elements: map[H:Hydrogen Li:Lithium]
}
我们也可以创建一个空的映射:emptyMap := map[string]float64{}
3. 映射中的零值
Go语言中不需要put,而是直接使用就行了,只要创建了就算拿了不存在的key,也会初始化为零值
package main
import "fmt"
func main() {
counters := make(map[string]int)
fmt.Printf("counters[\"aaa\"]: %v\n", counters["aaa"]) //counters["aaa"]: 0
strings := make(map[string]string)
fmt.Printf("strings[\"aaa\"]: %#v\n", strings["aaa"]) //strings["aaa"]: ""
}
4. 映射变量的零值是nil
如果我们声明了一个映射但是没有赋值,那么这个映射就是nil,再使用之前我们必须调用make函数进行创建映射
package main
import "fmt"
func main() {
var nilMap map[int]string
fmt.Printf("nilMap: %#v\n", nilMap) //nilMap: map[int]string(nil)
nilMap[3] = "aaa" //panic: assignment to entry in nil map
}
5. 如何区分已经赋值的值和零值
有时候虽然有零值,但是我们还是要区分到底这个key有没有赋值过。有这么一个场景:在根据分数判断是否及格的时候,我们传入一个名字,可能这个名字还没有存入map里面,但是由于初值赋值为0了,所以得到的结果就会认为是不及格,那么如何解决?
map集合获取value的时候会返回两个参数,第一个是value,第二个是是否已存在,那么我们就可以通过第二个参数来判断,代码如下
package main
import "fmt"
func main() {
counters := map[string]int{"a":3, "b":2}
var value int
var ok bool
value, ok = counters["a"]
fmt.Println(value, ok) //3 true
value, ok = counters["b"]
fmt.Println(value, ok) //2 true
value, ok = counters["c"]
fmt.Println(value, ok) //0 false
}
所以这种写法就是说尽管我们赋值成0了,但是还是可以通过第二个参数判断出来有没有赋值过
6. 对映射进行for…range循环
变量的格式:for key, value := range myMap{}
, key就是key,value就是value,myMap就是map集合
package main
import "fmt"
func main() {
counters := map[string]int{"a":3, "b":2}
for key, value:=range counters{
fmt.Println(key, ":", value)
//a : 3
//b : 2
}
}
如果需要循环所有的键,你可以忽略它对应的值变量:
package main
import "fmt"
func main() {
counters := map[string]int{"a":3, "b":2, "c":4, "d":5}
//单单处理值
for value :=range counters{
fmt.Printf("value: %v\n", value)
//value: a
//value: b
//value: c
//value: d
}
}
如果需要值,那么可以使用空白标识符处理键:
package main
import "fmt"
func main() {
counters := map[string]int{"a":3, "b":2, "c":4, "d":5}
//单单处理值
for _, grade :=range counters{
fmt.Printf("grade: %v\n", grade)
//grade: 3
//grade: 2
//grade: 4
//grade: 5
}
}
有一点就是说for...range
不是有序的,当我们运行次数够多的时候,会发现出现这种输出结果,和之前的不是一样的,结论就是:如果我们需要输出有序的集合顺序,就要自己定义,但是如果我们对输出顺序没有需求,就可以使用 for…range
如有错误,欢迎指出!!!