Go语言中提供的映射关系容器为map ,
-
Go中内置类型,其内部使用散列表(hash) 实现,为引用类型。
-
无序键值对(key-value)集合,通过key(类似索引)快速检索数据
-
必须初始化才能使用。
1.1 map是什么?
Map是一种数据结构,是一个集合,用于存储一系列无序的键值对。
基于键存储的,可以快速快速检索数据,键指向与该键关联的值
1.2 map的内部实现
Map存储的是什么?Map存储的是无序的键值对集合。
Map基于什么?基于散列表(hash表),故每次迭代Map,打印的key和value是无序的,每次迭代不一样。
Map散列表特点是?包含一组桶,每次存储和查找键值对的时候,都要先选择一个桶。
如何选择桶?把指定的键传给散列函数,就可索引到相应的桶,进而找到对应的键值。
这种方式的好处是?存储的数据越多,索引分布越均匀,所以我们访问键值对的速度也就越快
1.3 map声明和定义
Go语言中 map 的定义语法如下:
map[KeyType]ValueType
其中,
- KeyType:表示键的类型。
- ValueType:表示键对应的值的类型。
例子:
//var a map[key的类型]value类型
var a map[string]int
var b map[int]string
var c map[float32]string
注意:
1.声明是不会分配内存的,需要make初始化。
2.map必须初始化才能使用,否则panic。
3.map中声明value是什么类型,就存什么类型,混合类型自身不支持(Go是强类型语言),interfice支持存混合类型数据。
map类型的变量未初始化前默认为nil,所以需要使用make分配map内存,然后才能使用,不然会panic(异常)
实例如下:
实例1-1
package main
import (
"fmt"
)
func main() {
var a map[string]int
if a == nil { //map未初始化,其初始默认为nil
fmt.Println("map is nil. Going to make one.")
a = make(map[string]int)
}
}
执行结果如下:
另外也验证一下不初始化跑出panic
实例如下:
实例1-2
package main
import (
"fmt"
)
func main() {
var user map[string]int
user["abc"] = 38
fmt.Println(user)
}
执行结果如下:
已经抛出了panic,所以必须要初始化。
1.4 map初始化
1.4.1 方法1 make
map类型的变量默认初始值为nil,需要使用make()函数来分配内存。语法为:
make(map[KeyType]ValueType, [cap])
其中cap表示map的容量,该参数虽然不是必须的,但是我们应该在初始化map的时候就为其指定一个合适的容量。
实例:
package main
import (
"fmt"
)
func main() {
var user map[string]int = make(map[string]int, 5000) //初始化时可以指定容量也可以不指定,指定的话可以提升性能
//user := make(map[string]int) //也可以写成这样,多种方式
user["abc"] = 38
fmt.Println(user)
}
执行结果:
1.4.2 方法2 声明时进行初始化(借助常量)
m:= map[string]int{"张三":21}
m2:= map[string]int{"张三":21,"李四":22}
m3:= map[string]int{} //空map
实例:
package main
import (
"fmt"
)
func main() {
a := map[string]int{
"steve": 12000,
"jamie": 15000,
}
a["mike"] = 9000
fmt.Println("a map contents:", a)
}
执行结果:
1.6 map扩容
map扩容实际上类似切片扩容就是:
map本来的容量是4,现在容量不够了,map内部自动扩容,比如说扩容到8,其在底层的机制就是将旧的内存地址中的4个元素拷贝到新到容量为8的内存地址中,然后再继续接收新元素并使用。
所以说:在初始化时,如果我们知道map大概有多少元素时,可以初始化时指定,这样可以在一定程度上提升性能(频繁扩容影响性能)
1.7 如何访问map中的元素
通过key访问map中的元素
package main
import "fmt"
func main() {
a := map[string]int{
"steve": 12000,
"jamie": 15000,
}
a["mike"] = 9000
b := "jamie"
fmt.Println("Salary of", b, "is", a[b])
}
执行结果:
1.8 map添加键值对
map中的数据都是成对出现的,map的基本使用示例代码如下:
func main() {
scoreMap := make(map[string]int, 8)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
fmt.Println(scoreMap)
fmt.Println(scoreMap["小明"])
fmt.Printf("type of a:%T\n", scoreMap)
}
输出:
map[小明:100 张三:90]
100
type of a:map[string]int
map也支持在声明的时候填充元素,例如:
func main() {
userInfo := map[string]string{
"username": "哈登",
"password": "123456",
}
fmt.Println(userInfo) //
}
1.9 判断某个键(key)是否存在
Go语言中有个判断map中键是否存在的特殊写法,
格式如下:
value, ok := map[key]
相当于做一个白名单,去判断map中指定key是否存在
注意:ok仅仅是个变量,可以随便命名
举个例子:
func main() {
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
// 如果key存在ok为true,v为对应的值;不存在ok为false,v为值类型的零值
v, ok := scoreMap["张三"]
if ok {
fmt.Println(v)
} else {
fmt.Println("查无此人")
}
}
实例2:
用户白名单
package main
import (
"fmt"
)
var whiteUser map[int]bool = map[int]bool{
32323: true,
32011: true,
10222: true,
}
func isWhiteUser(userId int) bool {
_, ok := whiteUser[userId] //这里不需要返回value,所以直接_忽略
return ok
}
func main() {
userId := 100021
if isWhiteUser(userId) {
fmt.Printf("is white user:%v\n", userId)
} else {
fmt.Printf("is normal user:%v\n", userId)
}
}
执行结果:
1.10 map的遍历
Go语言中使用for range 遍历map。
range返回key value并赋值给变量,结合数组、切片遍历也是for range,其实数组及切片就是个特殊map。
func main() {
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["娜扎"] = 60
for k, v := range scoreMap {
fmt.Println(k, v)
}
}
但我们只想遍历key的时候,可以按下面的写法:
package main
import "fmt"
func main() {
scoreMap := map[string]int{
"张三": 90,
"小明": 100,
"张飞": 80,
}
for k := range scoreMap {
fmt.Println(k)
}
}
注意: 遍历map时输出的元素顺序与填充顺序无关。
1.11 使用delete()函数删除键值对
使用delete() 内建函数从map中删除一组键值对,delete() 函数的格式如下:
delete(map, key)
其中, map:表示要删除键值对的map ,key:表示要删除的键值对的键
示例代码如下:
func main(){
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["娜扎"] = 60
delete(scoreMap, "小明")//将小明:100从map中删除
for k,v := range scoreMap{
fmt.Println(k, v)
}
}
实例2:
package main
import (
"fmt"
)
func main() {
a := map[string]int{
"steve": 12000,
"jamie": 15000,
}
a["mike"] = 9000
fmt.Println("map before deletion", a)
delete(a, "steve")
fmt.Println("map after deletion", a)
}
执行结果:
1.12 map的长度
借助len函数
代码示例:
package main
import (
"fmt"
)
func main() {
a := map[string]int{
"steve": 12000,
"jamie": 15000,
}
a["mike"] = 9000
fmt.Println("length is", len(a))
}
执行结果:
1.13 按照指定顺序遍历map(扩展)
func main() {
rand.Seed(time.Now().UnixNano()) //初始化随机数种子
var scoreMap = make(map[string]int, 200)
for i := 0; i < 100; i++ {
key := fmt.Sprintf("stu%02d", i) //生成stu开头的字符串
value := rand.Intn(100) //生成0~99的随机整数
scoreMap[key] = value
}
//取出map中的所有key存入切片keys
var keys = make([]string, 0, 200)
for key := range scoreMap {
keys = append(keys, key)
}
//对切片进行排序
sort.Strings(keys)
//按照排序后的key遍历map
for _, key := range keys {
fmt.Println(key, scoreMap[key])
}
}
1.14 map是引用类型
通过下面这个例子验证map是引用类型
代码示例如下:
package main
import (
"fmt"
)
func main() {
a := map[string]int{
"steve": 12000,
"jamie": 15000,
}
a["mike"] = 9000
fmt.Println("origin map", a)
b := a
b["mike"] = 18000
fmt.Println("a map changed", a)
}
执行结果如下:
解释:
可以发现a为map,将a赋值给b,b也是map,map b做了修改,此时打印a,map a也发生了变化,这证明map是引用类型
1.7 元素为map类型的切片
下面的代码演示了切片中的元素为map类型时的操作:
实例1:
func main() {
var mapSlice = make([]map[string]string, 3)
for index, value := range mapSlice {
fmt.Printf("index:%d value:%v\n", index, value)
}
fmt.Println("after init")
// 对切片中的map元素进行初始化
mapSlice[0] = make(map[string]string, 10)
mapSlice[0]["name"] = "哈登"
mapSlice[0]["password"] = "123456"
mapSlice[0]["address"] = "休斯顿"
for index, value := range mapSlice {
fmt.Printf("index:%d value:%v\n", index, value)
}
}
实例2:
package main
import (
"fmt"
)
func main() {
var mapSlice []map[string]int
mapSlice = make([]map[string]int, 5) //此时是一个未初始化的map类型切片,所以需要先将切片初始化
fmt.Println("before map init")
for index, value := range mapSlice {
fmt.Printf("index:%d value:%v\n", index, value)
}
fmt.Println()
mapSlice[0] = make(map[string]int, 10) //要使用map,需要将map进行初始化。
mapSlice[0]["a"] = 1000
mapSlice[0]["b"] = 2000
mapSlice[0]["c"] = 3000
mapSlice[0]["d"] = 4000
mapSlice[0]["e"] = 5000
fmt.Println("after map init")
for index, value := range mapSlice {
fmt.Printf("index:%d value:%v\n", index, value)
}
}
执行结果:
1.8 值为切片类型的map
下面的代码演示了map中值为切片类型的操作:
package main
import "fmt"
func main() {
var sliceMap = make(map[string][]string, 3)
fmt.Println(sliceMap)
fmt.Println("after init")
key := "中国"
value, ok := sliceMap[key]
if !ok {
value = make([]string, 0, 2)
}
value = append(value, "北京", "上海")
sliceMap[key] = value
fmt.Println(sliceMap)
}
执行结果:
|
请发表评论