9.map
1.map是一种key:value键值对的数据结构容器。map内部实现是哈希表(hash)
2.map最重要的一点是通过key来快速检索数据,key类似于索引,指向数据的值,查询的时间复杂度为O(1)
3.map是引用类型的 默认值是nil即map[]
map的语法格式
可以使用内置函数make()还可以使用map关键字来定义map
//声明变量,默认mapA是nil 即map[]
var mapA map [int] int //变量名 key的数据类型 值的数据类型
//使用make函数声明变量 默认mapB是nil 即map[] 可以指定长度
var mapB = make(map[int]int)
//初始化
var mapC = map[int]int{1,2,3}
var mapC = make(map[int]int){1,2,3}
mapD := map[int]int{1,2,3}
mapD := make(map[int]int){1,2,3}
访问map
可以通过下标key获得值,直接访问
mapA := map[string]string{
"username": "小A",
"age": "18",
}
fmt.Printf("mapA["username"]: %v\n", mapA["username"])
fmt.Printf("mapA["age"]: %v\n", mapA["age"])
判断某个键是否存在
go语言中有个判断map中键是否存在的特殊写法,格式如下:
value,ok:=map[key]
如果ok为true,存在;否则不存在
mapA := make(map[string]string)
mapA["username"] = "小A"
value, ok := mapA["username"]
fmt.Printf("value:%v--ok:%v", value, ok)//小A true
value, ok = mapA["age"]
fmt.Printf("value:%v--ok:%v", value, ok)//无
false
遍历map
可以用for range 循环进行map遍历,得到key和value值
//1.遍历key
mapA := make(map[string]string)
mapA["username"] = "小A"
mapA["age"] = "20"
for key := range mapA {
fmt.Printf("key:%v", key)
}
//2.遍历key 和 value
mapA := make(map[string]string)
mapA["username"] = "小A"
mapA["age"] = "20"
for key, value := range mapA {
fmt.Println(key + ":" + value)
}
map的增删改查
增改查很常规跟数组和切片一样操作这里就不展示了主要展示删(delete)
使用delete()内建函数从map中删除一组键值对,delete()函数的格式如下:
delete(map 对象,key)
其中:
- map对象:表示要删除键值对的map对象
- key:表示要删除的键值对的键
mapA := make(map[string]string)
mapA["username"] = "小明"
mapA["age"] = "18"
delete(mapA, "age")
for k, v := range mapA {
fmt.Println(k + ":" + v)
}
元素为map类型的切片
我们想在切片里面放一系列用户的信息,这时候我们就可以定义一个元素为map类型的切片
sliceA := make([]map[string]string, 2, 2) //map[string]string看作一个整体
if sliceA[0] == nil {
sliceA[0] = make(map[string]string)
sliceA[0]["username"] = "小A" //这里的sliceA[0]看作map
sliceA[0]["age"] = "20"
sliceA[0]["weight"] = "60kg"
}
if sliceA[1] == nil {
sliceA[1] = make(map[string]string)
sliceA[1]["username"] = "小B" //这里的sliceA[0]看作map
sliceA[1]["age"] = "21"
sliceA[1]["weight"] = "65kg"
}
for _, v := range sliceA {
fmt.Println(v) //map[age:18 username:小A weight:60kg]
// map[age:19 username:小B weight:65kg]
}
遍历元素为map类型的切片
sliceA := make([]map[string]string, 2, 2) //map[string]string看作一个整体
if sliceA[0] == nil {
sliceA[0] = make(map[string]string)
sliceA[0]["username"] = "小A" //这里的sliceA[0]看作map
sliceA[0]["age"] = "20"
sliceA[0]["weight"] = "60kg"
}
if sliceA[1] == nil {
sliceA[1] = make(map[string]string)
sliceA[1]["username"] = "小B" //这里的sliceA[0]看作map
sliceA[1]["age"] = "21"
sliceA[1]["weight"] = "65kg"
}
for _, v1 := range sliceA {
for i, v2 := range v1 {
fmt.Printf("索引:%v--值:%v\n", i, v2)
/*
索引:username--值:小A
索引:age--值:20
索引:weight--值:60kg
索引:username--值:小B
索引:age--值:21
索引:weight--值:65kg
*/
}
}
值为切片类型的map
map的值可以是一个还可以是一系列(即值为切片类型)
var mapA = make(map[string][]string)//值是切片类型
mapA["hobby"] = []string{
"MarsCode",
"字节",
"AI练中学",
}
mapA["work"] = []string{
"golang",
"cpp",
"python",
}
for _, v := range mapA {
for index, value := range v {
fmt.Printf("index:%v--value:%v\n", index, value)
}
}
map的排序
map的排序默认是随机的
//1.把map的key放在切片里面
var mapA = make(map[int]int)
mapA[1]=14
mapA[3]=15
mapA[2]=92
mapA[5]=65
mapA[4]=35
var keySlice [] int
for key,_:=range mapA{
keySlice = append(keySlice,key)
}
//2.让key进行升序排序
sort.Ints(keySlice)
//3.循环遍历key输出map的值
for _,v:= range keySlice{
fmt.Printf("key:%v--value:%v",v,map[v])
}
//写一个程序,统计一个字符串中每个单词出现的次数
var str = "how do you do"
var strSlice = strings.Split(str, " ")
fmt.Println(strSlice)
var strMap = make(map[string]int)
for _, v := range strSlice {
strMap[v]++
}
fmt.Printf("strMap: %v\n", strMap)
10.函数
函数在go语言中是一级公民所有的功能单元都定义在函数中,可以重复使用。函数包含函数的名称,参数列表
和返回值类型,这些构成了函数的签名(signature)
go语言中函数特性
- go语言中有3种函数:普通函数,匿名函数(没有名称的函数),方法(定义在struct上的函数)。
- go语言中不允许函数的重载(overload),也就是说不允许函数同名。
- go语言种的函数不能嵌套函数,但可以嵌套匿名函数。
- 函数是一个值,可以将函数复制给变量,使得这个变量也成为函数
- 函数可以作为参数传递给另一个函数
- 函数的返回值可以是一个函数
- 函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数
- 函数参数可以没有名称
go语言函数定义语法
函数在使用之前必须先定义,可以调用函数来完成某个任务。函数可以重复调用,从而达到代码重用
func function_name([parameter list])[return_types]{
/*函数体*/
}
说明:
-
func:函数由func开始声明 -
function_name:函数名称,函数名和参数列表一起构成了函数签名 -
[parameter list]:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型,顺序,及参数个数。参数是可选的,也就是说函数也可以不包括参数。
-
return_types:返回类型,函数返回一列值。return_types是该列值的数据类型。有些功能不需要返回值,这种情况下return_types不是必须的 -
函数体:函数定义的代码集合
函数的返回值
函数可以有0或多个返回值,返回值需要指定数据类型,返回值通过return关键词来指定
return可以有参数,也可以没有参数,这些返回值可以有名称,也可以没有名称。go种的函数可以有多个返回值。
-
return关键词中指定了参数时,返回值可以不用名称。如果return省略参数,则返回值部分必须带名称 -
当返回值有名称时,必须使用括号包围,逗号分割,即使只有一个返回值
-
但即使返回值命名了,
return中也可以强制指定其他返回值名称,也就是说return的优先级更高 -
命名的返回值是预先声明好的,在函数内部可以直接使用,无需再次声明。命名返回值的名称不能和函数参数名称相同
否则报错提示变量重复定义
-
return中可以有表达式,但不能出现赋值表达式,这和其他语言可能有所不同。例如return a+b是正确的但
return c=a+b是错误的
go语言函数返回值实例
没有返回值
func f1(){
fmt.Printf("no return")
}
有一个返回值
func sum(a int,b int)(ret int){
ret = a + b
return ret //return
}
func sum(a int,b int)(int){
ret:=a+b
return ret
}
多个返回值,且在return中指定返回的内容
func f2()(name string,age int){
name = "小A"
age=20
return name,age
}
多个返回值,返回值名称没有被使用
func f3()(name string,age int){
name = "小A"
age=20
return //等价于 return name,age
}
总结:
-
返回值类型 声明了返回值变量如果和函数内用的一样 则函数体内可以直接使用该变量 且可以直接return结果
-
返回值类型那部分可以直接写数据类型不写变量名称 这样函数体内部需要重新声明 不能直接用return返回结果
-
go中经常会使用其中一个返回值作为函数是否执行成功,是否有错误信息的判断条件。例如
return value,existsreturn value,ok,return value,err -
当函数的返回值过多时,例如有4个以上的返回值,应该将这些返回值收集到容器中,然后以返回容器的方式去返回。例如
同类型的返回值可以放进slice中,不同类型的返回值可以放进map中
-
但函数有多个返回值时,如果其中某个或某几个返回值不想使用,可以通过下划线
_来丢弃这些返回值。例如下面的
f1函数两个返回值,调用该函数时,丢弃了第二个返回值b,只保留了第一个返回值a赋值给了变量a。func test()(a int,b int){ a=1 b=2 return } func main(){ a,_:=test() }
go函数的参数
go语言函数可以有0或多个参数,参数需要指定数据类型
声明函数的参数列表叫做形参,调用时传递的参数叫做实参。
go语言是通过传值的方式传参的,意味着传递给函数的是拷贝后的副本,所以函数内部访问,修改的也是这个副本
不修改原值
go语言可以使用边长参数,有时候不能确定参数的个数,可以使用变长参数,可以在函数定义语句的参数部分使用
ARGS...TYPE。这时会将...代表的参数全部保存到一个名为ARGS的slice中,注意这些参数的数据类型都是TYPE
演示参数传递,按值传递
func test2(a int, b int) { //形参列表
temp := a
a = b
b = temp
}
func main() {
a := 6
b := 9
fmt.Println(a, b)//实参列表
test2(a, b)
fmt.Println(a, b)
}
从运行结果可以看到,调用函数test后,a,b的值并没有被改变,说明参数传递是拷贝了一个副本,也就是拷贝了一份新的内容进行运算
map,slice,interface,channel这些数据类型本身就是指针类型的,所以就算是拷贝传值也是拷贝的指针,拷贝后的参数仍然指向
底层数据结构,所以修改它们可能会影响外部数据结构的值
演示参数传递,按址传递
func test3(a []int) {
a[0] = 49
}
func main() {
a := []int{1, 2, 3}
test3(a)
fmt.Println(a)
}
变长参数
func f1(args ...int){
for_,v:=range args{
fmt.Println(v)
}
}
func f2(name string,age int,args ...int){
fmt.Println(name)
fmt.Println(age)
for _,v:= range args{
fmt.Println(v)
}
}
func main(){
f1(1,2,3)
fmt.Println("-------")
f1(1,2,3,4,5,6)
fmt.Println("-------")
f2("小A",20,1,2,3)
}
go语言函数类型和函数变量
可以使用type关键字来定义一个函数类型,语法格式如下:
type fun func(int,int)int
上面语句定义了一个fun函数类型,它是一种函数类型,这种函数接收两个int类型的参数并返回一个int类型的返回值
下面我们定义两个这样结构的两个函数,一个求和,一个比较大小:
func sum (a int,b int)int{
return a+b
}
func max(a int, b int)int{
if a>b {
return a
} else {
return b
}
}
下面定义一个fun函数类型,把sum和max赋值给它
type fun func(int, int) int//fun = func(int,int) int
func sum(a int, b int) int {
return a + b
}
func max(a int, b int) int {
if a > b {
return a
} else {
return b
}
}
func main() {
var f fun//f = fun = func(int,int)int
f = sum
s := f(2, 2)
fmt.Println(s)
f = max
m := f(3, 3)
fmt.Println(m)
}
go语言的高阶函数
go语言的函数,可以作为函数的参数,传递给另外一个函数,可以作为另外一个函数的返回值返回
go语言函数作为参数
func sayHello(name string){
fmt.Printf("Hello,%s",name)
}
func f1(name string,f func(string)){
f(name)
}
func main(){
f1("小A",sayHello) //Hello,小A
}
go语言函数作为返回值
func add(a int, b int) int {
return a + b
}
func sub(a int, b int) int {
return a - b
}
func cal(operator string) func(int, int) int {
switch operator {
case "+":
return add
case "-":
return sub
default:
return nil
}
}
func main() {
f1 := cal("+")
add := f1(1, 2)
f2 := cal("-")
sub := f2(2, 1)
fmt.Println(add)
fmt.Println(sub)
}
go语言的匿名函数
go语言函数不能嵌套,但是在函数内部可以定义匿名函数,实现一下简单功能调用。
所谓匿名函数就是,没有名称的函数
语法格式如下:
func (参数列表)(返回值)
当然可以没有参数列表和返回值
匿名函数实例
func main(){
max:=func(a,int,b int)int{
if a>b{
return a
}else{
return b
}
}
i:=max(1,2)
fmt.Println(i)
}
自己执行
func main(){
func(a int,b int){
max:=0
if a>b{
max=a
}else{
max=b
}
fmt.Println(max)
}(1,2)//func(){}可以看作是一个函数名称如f1 f1里是函数的执行过程 即 func main{ f1(1,2)}自动执行
}
func test1(){
name:="小A"
age:="18"
f1:=func()string{
return name+age
}//f1是变量,变量内容是匿名函数,不是返回值。f1调用之后f1()才有返回值
msg:=f1()
fmt.Println(msg)
}
今天就写到里了,欢迎大家指正!