目录
引入
复合类型 值的类型 值的数量 值的索引
数组 相同 固定 下标
切片 相同 动态 下标
结构体 不相同 固定 属性名
Map 相同 动态 key键
map(key:value) k:v
key:除了切片等引用类型,其他类型即可
value:可以是所有类型的值
Map是一种数据结构,用于存储一系列无序的键值对,Map基于键来存储值。
Map功能强大的地方是,能够基于键快速检索数据。键就像索引一样,指向与该键关联的值。
与C++、Java不一样,Go使用Map不需要引入任何库。
Map的实现
因为映射也是一个数据集合,所以也可以使用类似处理数组和切片的方式来迭代映射中的元素。但映射是无序集合,所以即使以同样的顺序保存键值对,每次迭代映射时,元素顺序也可能不一样。无序的原因是映射的本质使用了散列表。
map在底层是用哈希(hash)表实现的,在C:\Program Files\Go\src\hash\maphash\maphash.go,map是一个hash数组列表,由一个个bucket组成,示意图如下:
每一个元素都被称为bucket的结构体,每一个bucket可以保存8个键值对,所有元素被hash算法填入到数组的bucket中,bucket填满后,将通过一个overflow指针来扩展一个bucket,从来形成链表,以此来解决hash冲突的问题,map就是一个bucket指针型的一维数组。
创建map语法如下:
//创建一个映射,键的类型string,值的类型int
dict : mmake(map[ string]int)
//创建一个映射,键值类型都是string,并对两个键值对进行初始化
dict:=map[string]stringi""name" : "zhangsan" , "address" : "nanjing"}
映射的键可以是任何值,这个值的类型并不限制,内置类型或者结构体都可以,需要确定这个值可以使用==运算符做比较。需要注意的是,切片、函数以及包含切片的结构类型由于是引用类型,均不能作为映射的键。
dict : =map[[]string]int
//报错: incomparable map key type []string
//切片作为值,不可以作为键
dict:=map[int][ ]string
Map的基本使用
map中的数据都是成对出现的
package main
import "fmt"
func main() {
var soMap = make(map[string]int, 8)
soMap["老王"] = 45
soMap["隔壁"] = 7
fmt.Println(soMap)
fmt.Println(soMap["隔壁"])
fmt.Printf("类型是:%T", soMap)
}
运行结果
map[老王:45 隔壁:7]
7
类型是:map[string]int
还可以在声明时候添加map
package main
import "fmt"
func main() {
var Cmap = map[string]string{
"xiaoming": "mingxiao",
"zhangsan": "sanzhang",
}
fmt.Println(Cmap)
}
运行结果
map[xiaoming:mingxiao zhangsan:sanzhang]
查找和判断某个值是否存在
从Map取值时有两种方式。
第一种方式是,获得值以及一个表达这个值是否存在的标志。
package main
import "fmt"
func main() {
var soMap = make(map[string]int, 8)
soMap["老王"] = 45
soMap["隔壁"] = 7
key, value := soMap["老王"]
fmt.Println(key, value)
}
运行结果
45 true
第二种方式是,只返回键对应的值,再判断这个值是否有零值,以此来确定键是否存在。这种方式 只能用在映射存储的值都是非零值的情况。
package main
import "fmt"
func main() {
var soMap = make(map[string]string, 8)
soMap["老王"] = "45"
soMap["隔壁"] = "7"
value := soMap["老王"]
if value != " " {
fmt.Println(value)
}
}
运行结果
45
判断某个值是否存在
package main
import "fmt"
func main() {
var soMap = make(map[string]string, 8)
soMap["老王"] = "45"
soMap["隔壁"] = "7"
value, ok := soMap["隔壁"]
if ok {
fmt.Println(value)
} else {
fmt.Println("没有这个人")
}
}
运行结果
7
package main
import "fmt"
func main() {
var soMap = make(map[string]string, 8)
soMap["老王"] = "45"
soMap["隔壁"] = "7"
value, ok := soMap["老李"]
if ok {
fmt.Println(value)
} else {
fmt.Println("没有这个人")
}
}
运行结果
没有这个人
遍历Map
使用rang迭代Map里面所有值
package main
import "fmt"
func main() {
var soMap = make(map[string]string, 8)
soMap["老王"] = "45"
soMap["隔壁"] = "7"
for key, value := range soMap {
fmt.Println(key, value)
}
}
运行结果
老王 45
隔壁 7
但我们只想遍历key的时候,可以这样写
package main
import "fmt"
func main() {
var soMap = make(map[string]string, 8)
soMap["老王"] = "45"
soMap["隔壁"] = "7"
for key := range soMap {
fmt.Println(key)
}
/*或者也可以
for key,_:= range soMap {
fmt.Println(key)
}*/
}
运行结果
老王
隔壁
注意:遍历map时的元素顺序与添加键值对的顺序无关。
元素删除
内置函数delete()用于蒯除容器内的元素,
delete()函数的格式如下:
delete(map, key)
其中,
map:表示要删除键值对的map
key:表示要删除的键值对的键
从map中删除键为key的键值对。如果key这个键不存在,那么这个调用将什么都不发生,也不会有任何副作用。但是如果传入的map变量的值是nil,该调用将导致程序抛出异常(panic).
package main
import "fmt"
func main() {
var soMap = make(map[string]string, 8)
soMap["老王"] = "45"
soMap["隔壁"] = "7"
soMap["张三"] = "10086"
soMap["李四"] = "10010"
for key, value := range soMap {
fmt.Println(key, value)
}
delete(soMap, "隔壁")
fmt.Println("------------------删除之后----------------")
for key, value := range soMap {
fmt.Println(key, value)
}
}
运行结果
老王 45
隔壁 7
张三 10086
李四 10010
------------------删除之后----------------
老王 45
张三 10086
李四 10010
按照指定顺序遍历Map
package main
import (
"fmt"
"math/rand"
"sort"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano()) //随机数
var doMap = make(map[string]int, 100)
for i := 0; i < 10; i++ {
name := fmt.Sprintf("ns%d", i) //生成ns开头的数字
age := rand.Intn(50) //随机生成0-50的数字作为年龄
doMap[name] = age
}
//取出map的值放入到切片中
var key = make([]string, 0, 100)
for name := range doMap {
key = append(key, name)
}
//对切片进行排序
sort.Strings(key)
//排完序进行遍历
for _, name := range key {
fmt.Println(name, doMap[name])
}
}
运行结果
ns0 18
ns1 27
ns2 19
ns3 46
ns4 2
ns5 44
ns6 1
ns7 11
ns8 30
ns9 34
元素为map类型的切片
package main
import "fmt"
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("----------------初始化之后---------------------")
// 对切片中的map元素进行初始化
mapSlice[0] = make(map[string]string, 10)
mapSlice[0]["name"] = "晓彬"
mapSlice[0]["password"] = "123456"
mapSlice[0]["address"] = "北京东路"
mapSlice[1] = make(map[string]string, 10)
mapSlice[1]["name"] = "小红"
mapSlice[1]["password"] = "123456"
mapSlice[1]["address"] = "南京路"
mapSlice[2] = make(map[string]string, 10)
mapSlice[2]["name"] = "小王"
mapSlice[2]["password"] = "123456"
mapSlice[2]["address"] = "南京西路"
for index, value := range mapSlice {
fmt.Printf("index:%d value:%v\n", index, value)
}
}
运行结果
index:0 value:map[]
index:1 value:map[]
index:2 value:map[]
----------------初始化之后---------------------
index:0 value:map[address:北京东路 name:晓彬 password:123456]
index:1 value:map[address:南京路 name:小红 password:123456]
index:2 value:map[address:南京西路 name:小王 password:123456]
值为切片类型的map
package main
import "fmt"
func main() {
var sliceMap = make(map[string][]string, 3)
fmt.Println(sliceMap)
fmt.Println("-----------------初始化之后-------------")
key := "中国"
value, ok := sliceMap[key]
if !ok {
value = make([]string, 0, 2)
}
value = append(value, "北京", "上海")
sliceMap[key] = value
fmt.Println(sliceMap)
}
运行结果
map[]
-----------------初始化之后-------------
map[中国:[北京 上海]]
将Map传递到函数
在函数间传递Map并不会制造出该映射的副本。当传递Map给函数,并对这个Map做了修改时,所有对这个Map的引用都会察觉到这个修改。
package main
import "fmt"
func main() {
disc := map[string]int{"1": 10, "2": 20, "3": 30, "4": 0, "5": 0}
//遍历map中所有值
for k, v := range disc {
fmt.Printf("key=%s,value=%d\n", k, v)
}
test(disc)
fmt.Println("--------------处理后-----------------")
for k, v := range disc {
fmt.Printf("key=%s,value=%d\n", k, v)
}
}
func test(m map[string]int) {
m["2"] = 40
}
运行结果
印证:遍历map时的元素顺序与添加键值对的顺序无关。而且map是引用类型