青训营后端Go语言基础(三) | 豆包MarsCode AI 刷题

97 阅读11分钟

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语言中函数特性

  1. go语言中有3种函数:普通函数,匿名函数(没有名称的函数),方法(定义在struct上的函数)。
  2. go语言中不允许函数的重载(overload),也就是说不允许函数同名。
  3. go语言种的函数不能嵌套函数,但可以嵌套匿名函数。
  4. 函数是一个值,可以将函数复制给变量,使得这个变量也成为函数
  5. 函数可以作为参数传递给另一个函数
  6. 函数的返回值可以是一个函数
  7. 函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数
  8. 函数参数可以没有名称

go语言函数定义语法

函数在使用之前必须先定义,可以调用函数来完成某个任务。函数可以重复调用,从而达到代码重用

func function_name([parameter list])[return_types]{
    /*函数体*/
}

说明:

  • func:函数由func开始声明

  • function_name:函数名称,函数名和参数列表一起构成了函数签名

  • [parameter list]:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。

    参数列表指定的是参数类型,顺序,及参数个数。参数是可选的,也就是说函数也可以不包括参数。

  • return_types:返回类型,函数返回一列值。return_types是该列值的数据类型。有些功能不需要返回值,这种情况下

    return_types不是必须的

  • 函数体:函数定义的代码集合

函数的返回值

函数可以有0或多个返回值,返回值需要指定数据类型,返回值通过return关键词来指定

return可以有参数,也可以没有参数,这些返回值可以有名称,也可以没有名称。go种的函数可以有多个返回值。

  1. return关键词中指定了参数时,返回值可以不用名称。如果return省略参数,则返回值部分必须带名称

  2. 当返回值有名称时,必须使用括号包围,逗号分割,即使只有一个返回值

  3. 但即使返回值命名了,return中也可以强制指定其他返回值名称,也就是说return的优先级更高

  4. 命名的返回值是预先声明好的,在函数内部可以直接使用,无需再次声明。命名返回值的名称不能和函数参数名称相同

    否则报错提示变量重复定义

  5. 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
}

总结:

  1. 返回值类型 声明了返回值变量如果和函数内用的一样 则函数体内可以直接使用该变量 且可以直接return结果

  2. 返回值类型那部分可以直接写数据类型不写变量名称 这样函数体内部需要重新声明 不能直接用return返回结果

  3. go中经常会使用其中一个返回值作为函数是否执行成功,是否有错误信息的判断条件。例如return value,exists

    return value,okreturn value,err

  4. 当函数的返回值过多时,例如有4个以上的返回值,应该将这些返回值收集到容器中,然后以返回容器的方式去返回。例如

    同类型的返回值可以放进slice中,不同类型的返回值可以放进map中

  5. 但函数有多个返回值时,如果其中某个或某几个返回值不想使用,可以通过下划线_来丢弃这些返回值。例如

    下面的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函数类型,把summax赋值给它

type fun func(int, int) int//fun = func(int,int) intfunc 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)
}

今天就写到里了,欢迎大家指正!