6. 容器类型

6. 容器类型

6.1 数组

    数组是一个固定长度特定相同类型元素组成的序列。其元素个数可以为>=0个。

在Go语言中,由于数组长度是固定,且缺乏一定的灵活性,在Go语言一般较少使用,通常都是使用切片

6.1.1 数组定义

    Go语言的数组定义如下所示:

// 方式一:在定义时即指定长度
var name [length]type
// 方式二:通过元素个数自动推断数组长度
var name = [...]type{ele1,ele2,...,elex}

    语法说明如下所示:

  • name: 定义数组的变量名
  • length: 定义的数组元素的个数,即整个数组的长度,必须为整型
  • type: 数组元素的数据类型

    数组的元素可以通过下标来访问。在Go语言中,数组下标的范围为[0,length-1],示例代码如下所示:

package main

import "fmt"

func main() {
    // 定义数组长度为5
    var s [5]int
    for i := 0; i < len(s); i++ {
        fmt.Printf("数组s的第%d个元素为%v\n", i, s[i])
    }
    // 修改元素的值
    for i := 0; i < len(s); i++ {
        s[i] = i
    }
    fmt.Printf("数组s的值为:%+v\n", s)

    // 根据数组元素自动推断长度
    a := [...]int{1, 2, 3, 4, 5, 6}
    fmt.Printf("数组a的长度为:%d,相应的元素为:%+v\n", len(a), a)
}

    代码运行结果如下所示:

数组s的第0个元素为0
数组s的第1个元素为0
数组s的第2个元素为0
数组s的第3个元素为0
数组s的第4个元素为0
数组s的值为:[0 1 2 3 4]
数组a的长度为:6,相应的元素为:[1 2 3 4 5 6]

6.1.2 数组初始化

    由于数组在定义时,Go语言会自动为数组元素设置零值,在需要修改其元素时,需要使用下标进行修改,会不太方便。因此可以在定义数组时,即为每个元素设置初始值,其语法格式如下所示:

var name = [length]type{ele1,ele2,...,elex}
var name = [...]type{ele1,ele2,...,elex}
// 在定义时,设置指定下标元素的值
var name = [...]type{index1:value1,index2:value2}

    示例代码如下所示:

package main

import "fmt"

func main() {
    // 定义数组时并初始化各个元素的值
    var s1 = [3]int{1, 2, 3}
    for i, v := range s1 {
        fmt.Printf("数组s1的下标为%d,元素为:%+v\n", i, v)
    }

    // 定义数组并根据元素个数自动推断数组长度
    var s2 = [...]int{4, 5, 6, 7}
    fmt.Printf("数组s2的长度为%d,元素为:%+v\n", len(s2), s2)

    // 定义数组,并设置部分元素的值
    var s3 = [...]int{0: 100, 10: 500}
    fmt.Printf("数组s2的长度为%d,元素为:%+v\n", len(s3), s3)

    // 定义数组,并设置部分元素的值
    var s4 = [8]int{0: 100, 5: 500}
    fmt.Printf("数组s2的长度为%d,元素为:%+v\n", len(s4), s4)
}

    运行结果如下所示:

数组s1的下标为0,元素为:1
数组s1的下标为1,元素为:2
数组s1的下标为2,元素为:3
数组s2的长度为4,元素为:[4 5 6 7]
数组s2的长度为11,元素为:[100 0 0 0 0 0 0 0 0 0 500]
数组s2的长度为8,元素为:[100 0 0 0 0 500 0 0]

    根据以上代码运行结果总结如下所示:

  • 在数组类型后面添加{},并根据数据类型设置元素的值,是最常用的方式
  • 使用 ... 设置数组长度,Go语言会根据最终的元素个数自动推测元素个数,使用这种方式,必须要在初始化时就要设置每个元素的初始值,否则编译无法通过
  • 如果只是对数组中个别元素设置初始值,可以使用下标:元素值方式。如果数组长度未明确指定,则自动获取最大的下标做为数组的长度。

6.1.3 多维数组

    多维数组是在一个数组中嵌套了多个数组,形成递进关系,语法格式如下所示:

var name [length1][length2]...[lengthx]type

    相应的参数说明如下所示:

  • name:多维数组的名称
  • lengthx:每一维度的数组元素个数
  • type:多维数组的数据类型

在日常开发中,二维和三维数组是比较常用的。通过二维数组可以实现类似于表格的功能,通过三维数组可以实现空间坐标系功能。

    示例代码如下所示:

package main

import (
    "fmt"
    "math"
)

func main() {
    var sheet = [3][2]int{{1, 2}, {3, 4}, {5, 6}}
    for i, v1 := range sheet {
        for j, v2 := range v1 {
            fmt.Printf("多维数组,第%d行长度为:%d,元素为:%v,第%d列,元素为:%v\n", i+1, len(v1), v1, j+1, v2)
        }
    }
    // 定义三维数组
    var location = [3][3][3]int{{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}, {{10, 20, 30}, {40, 50, 60}, {70, 80, 90}}}
    pointA := location[0][1]
    pointB := location[1][0]
    xDistance := math.Pow((float64(pointA[0]) - float64(pointB[0])), 2)
    yDistance := math.Pow((float64(pointA[1]) - float64(pointB[1])), 2)
    zDistance := math.Pow((float64(pointA[2]) - float64(pointB[2])), 2)
    distance := math.Sqrt(xDistance + yDistance + zDistance)
    fmt.Printf("pointA的会标为:%+v,pointB的会标为:%+v,对应的距离为:%+v\n", pointA, pointB, distance)
}

    代码运行结果如下所示:

多维数组,第1行长度为:2,元素为:[1 2],第1列,元素为:1
多维数组,第1行长度为:2,元素为:[1 2],第2列,元素为:2
多维数组,第2行长度为:2,元素为:[3 4],第1列,元素为:3
多维数组,第2行长度为:2,元素为:[3 4],第2列,元素为:4
多维数组,第3行长度为:2,元素为:[5 6],第1列,元素为:5
多维数组,第3行长度为:2,元素为:[5 6],第2列,元素为:6
pointA的会标为:[4 5 6],pointB的会标为:[10 20 30],对应的距离为:28.930952282978865

6.2 切片

    切片是一种特殊的数组结构,其可以实现动态数组功能,可以按需进行自动扩缩容其大小。底层仍然是以数组为基础进行创建的。

6.2.1 切片定义

    语法格式如下所示:

// 定义切片不赋值
var name []type
// 定义切片并赋值
var name = []type{ele1,ele2,...,elex}
// 使用make定义
var name = make([]type,len,cap)
// 使用make定义,简写
name:=make([]type,len)

    示例代码如下所示:

package main

import "fmt"

func main() {
    // 方式一:定义切片但不赋值
    var s1 []int
    // 方式二:定义切片并赋值
    var s2 = []int{1, 2, 3}
    // 使用make定义
    var s3 = make([]int, 3)
    // 使用简写定义
    s4 := make([]int, 2, 2)
    s5 := []int{4, 5}
    // 空切片这样赋值会报错
    // s1[1] = 100
    s2[1] = 200
    s3[1] = 300
    s4[1] = 400
    s5[1] = 500
    fmt.Printf("切片s1的值:%+v\n", s1)
    fmt.Printf("切片s2的值:%+v\n", s2)
    fmt.Printf("切片s3的值:%+v\n", s3)
    fmt.Printf("切片s4的值:%+v\n", s4)
    fmt.Printf("切片s5的值:%+v\n", s5)
}

    代码运行结果如下所示:

切片s1的值:[]
切片s2的值:[1 200 3]
切片s3的值:[0 300 0]
切片s4的值:[0 400]
切片s5的值:[4 500]

注意事项:若定义了一个空切片,不能采用下标的方式的修改切片相应的值

6.2.2 添加元素

    由于切片是动态数组,即使在定义的时候设置了切片长度,也可以向切片添加切片元素。向切片添加元素使用内置方法append,其定义格式如下所示:

ss:=append(slice,eles)
ss:=append(slice1,slice2...)
  • slice: 代表待新增元素的切片
  • eles:代表新增元素的值,数据类型需要与切片数据类型相同
  • ss: append方法返回的值,仍然为切片类型,若返回值与原有切片变量命名相同,则覆盖原有切片,如果不同,则为两个不同的切片

append方法也可以实现两个切片的拼接,但两个切片的数据类型需要一致

    示例代码如下所示:

package main

import "fmt"

func main() {
    var ss1 = []int{1, 2}
    fmt.Printf("新增切片元素前值:%+v\n", ss1)
    // 新增切片元素不覆盖原有切片
    ss2 := append(ss1, 3)
    fmt.Printf("新增切片元素后不覆盖原有切片的值:%+v\n", ss2)
    // 新增切片元素覆盖原有切片
    ss1 = append(ss1, 4)
    fmt.Printf("新增切片元素后覆盖原有切片的值:%+v\n", ss1)
    // 添加多个元素
    ss1 = append(ss1, 5, 6, 7, 8, 9)
    fmt.Printf("新增多个切片元素后覆盖原有切片的值:%+v\n", ss1)
    // 拼接多个切片
    ss3 := []string{"My", "name", "is", "Surpass"}
    ss4 := []string{"I", "am", "in", "Shanghai"}
    ss5 := append(ss3, ss4...)
    fmt.Printf("ss3的元素:%+v\n", ss3)
    fmt.Printf("ss4的元素:%+v\n", ss4)
    fmt.Printf("ss5的元素:%+v\n", ss5)
}

    代码运行结果如下所示:

新增切片元素前值:[1 2]
新增切片元素后不覆盖原有切片的值:[1 2 3]
新增切片元素后覆盖原有切片的值:[1 2 4]
新增多个切片元素后覆盖原有切片的值:[1 2 4 5 6 7 8 9]
ss3的元素:[My name is Surpass]
ss4的元素:[I am in Shanghai]
ss5的元素:[My name is Surpass I am in Shanghai]

6.2.3 截取元素

    如果只需要切片的部分元素,则需要使用切片截取功能,截取方式通常是使用下标进行定位和截取。语法如下所示:

s:=slice(startIndex,endIndex)
  • slice:待截取的切片
  • startIndex,endIndex:截取切片的起始和终止索引下标
  • s: 截取完成后的切片
  • 若未设置startIndex,则默认从下标0开始,若未设置endIndex,则默认截取到切片最后一个元素
  • 截取的规则是前包后不包,即 [startIndex,endIndex)

    示例代码如下所示:

package main

import "fmt"

func main() {
    ss := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
    // 截取所有元素
    s1 := ss[:]
    fmt.Printf("s1: %+v\n", s1)
    // 截取1-4元素的
    s2 := ss[1:5]
    fmt.Printf("s2: %+v\n", s2)
    // 截取2之后的所有元素
    s3 := ss[2:]
    fmt.Printf("s3: %+v\n", s3)
    // 截取第一个元素到第6个元素
    s4 := ss[:6]
    fmt.Printf("s4: %+v\n", s4)
    // 起始和终止位置相同时
    s5 := ss[2:2]
    fmt.Printf("s5: %+v\n", s5)
    // 通过切片截取完成筛选功能
    s6 := append(ss[:1], ss[6:]...)
    fmt.Printf("s6: %+v\n", s6)
}

    代码运行结果如下所示:

s1: [1 2 3 4 5 6 7 8 9]
s2: [2 3 4 5]
s3: [3 4 5 6 7 8 9]
s4: [1 2 3 4 5 6]
s5: []
s6: [1 7 8 9]

    根据以上运行结果,总结如下所示:

  • 截取切片的固定元素必须设置截取的起始和终止位置,且不能出现越界的情况
  • 截取切片时起始和终止位置相同时,截取的切片为空
  • 截取切片在此之前的元素,可以仅设置终止位置即可,截取在此之后所有元素同理
  • Go语言中,切片截取不支持负索引截取

6.2.4 切片复制

    Go语言内置方法copy可以将一个切片或数组复制到另一个切片或数组中,且需要两个切片的数据类型必须相同。如果两个切片或数组的长不同,程序按照复制的切片元素个数进行复制。语法格式如下所示:

s:=copy(slice1,slice2)
  • slice1: 待复制的切片
  • slice2: 被复制的切片
  • s: copy的返回值,代表复制的元素数量

    示例代码如下所示:

package main

import "fmt"

func main() {
    s1 := []int{1, 2, 3}
    s2 := []int{4, 5, 6}
    s3 := []int{7, 8, 9, 0}
    s4 := []int{10}
    // 将s2复制到s1
    n1 := copy(s1, s2)
    fmt.Printf("复制的元素数量:%d,复制后的切片s1值为:%+v\n", n1, s1)
    fmt.Printf("切片s2:%+v\n", s2)
    n2 := copy(s3, s2)
    fmt.Printf("复制的元素数量:%d,复制后的切片s3值为:%+v\n", n2, s3)
    n3 := copy(s4, s3)
    fmt.Printf("复制的元素数量:%d,复制后的切片s4值为:%+v\n", n3, s4)
}

    运行结果如下所示:

复制的元素数量:3,复制后的切片s1值为:[4 5 6]
切片s2:[4 5 6]
复制的元素数量:3,复制后的切片s3值为:[4 5 6 0]
复制的元素数量:1,复制后的切片s4值为:[4]

    根据运行结果,总结如下所示:

  • copy方法,第一个参数slice1的切片元素会被第二个参数slice2的切片元素覆盖
  • 如果参数slice1和参数slice2的切片元素个数不同,复制过程以参数slice1的切片元素个数为主
  • 如果参数slice1的切片元素个数大于参数slice2,则参数slice2的所有切片元素依次替换参数slice1的切片元素,参数slice1没有替换的切片元素则保持不变

以上总结为一句话,使用slice2的元素去替换slice1的切片元素,替换数量以slice1切片元素个数为主

6.2.5 切片长度和容量

    使用make方法定义切片时,必须设置切片的长度,但也有一个可选参数cap,用于设置切片容量,默认情况下,切片长度和容量相等。我们先来看看切片的原始代码定义:

// src/runtime/slice.go
type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

    从源码定义可以看出,切片的底层还是数组,其本质还是对底层数组的一个连续片断的引用,其内存模型示意图如下所示:

0601-切片内存模型.png
  • len: 表示切片长度,指的是切片里面目前有几个元素
  • cap: 表示给切片分配的内存空间可以容纳多少元素

    示例代码如下所示:

s1 := []int{1, 2, 3}
fmt.Printf("切片值:%+v,切片的内存地址:%p,切片第一个元素的地址:%p\n", s1, &s1, &s1[0])
fmt.Printf("切片长度:%d,容量为:%d\n", len(s1), cap(s1))

// 运行结果
// 切片值:[1 2 3],切片的内存地址:0xc0000a4030,切片第一个元素的地址:0xc0000ae030
// 切片长度:3,容量为:3

    在前面章节中,有使用append对切片进行扩容,在添加的元素超过其设置的长度后,会导致扩容。主要场景如下所示:

  • 底层共用一个数组,没有超过容量时,不需要扩容
package main

import "fmt"

func main() {
    s1 := make([]int, 3, 5)
    fmt.Printf("s1地址:%p,底层数组首地址:%p,切片长度:%-2d,容量:%-2d,切片值:%+v\n", &s1, &s1[0], len(s1), cap(s1), s1)
    // 生成一个新的切片
    s2 := append(s1, 1, 2)
    fmt.Printf("s1地址:%p,底层数组首地址:%p,切片长度:%-2d,容量:%-2d,切片值:%+v\n", &s1, &s1[0], len(s1), cap(s1), s1)
    fmt.Printf("s2地址:%p,底层数组首地址:%p,切片长度:%-2d,容量:%-2d,切片值:%+v\n", &s2, &s2[0], len(s2), cap(s2), s2)
    s3 := append(s1, -10)
    fmt.Printf("s3地址:%p,底层数组首地址:%p,切片长度:%-2d,容量:%-2d,切片值:%+v\n", &s3, &s3[0], len(s3), cap(s3), s3)
}

    运行结果如下所示:

s1地址:0xc000008048,底层数组首地址:0xc00000e420,切片长度:3 ,容量:5 ,切片值:[0 0 0]
s1地址:0xc000008048,底层数组首地址:0xc00000e420,切片长度:3 ,容量:5 ,切片值:[0 0 0]
s2地址:0xc000008078,底层数组首地址:0xc00000e420,切片长度:5 ,容量:5 ,切片值:[0 0 0 1 2]
s3地址:0xc0000080c0,底层数组首地址:0xc00000e420,切片长度:4 ,容量:5 ,切片值:[0 0 0 -10]

虽然使用同一个底层数组,但各自使用的片段不一样

  • 底层共用一个数组,超过容量时,需要扩容
package main

import "fmt"

func main() {
    s1 := make([]int, 3, 5)
    fmt.Printf("s1地址:%p,底层数组首地址:%p,切片长度:%-2d,容量:%-2d,切片值:%+v\n", &s1, &s1[0], len(s1), cap(s1), s1)
    s2 := append(s1, 1, 2, 3)
    fmt.Printf("s2地址:%p,底层数组首地址:%p,切片长度:%-2d,容量:%-2d,切片值:%+v\n", &s2, &s2[0], len(s2), cap(s2), s2)
}

    运行结果如下所示:

s1地址:0xc0000a4030,底层数组首地址:0xc0000b8030,切片长度:3 ,容量:5 ,切片值:[0 0 0]
s2地址:0xc0000a4060,底层数组首地址:0xc0000c2000,切片长度:6 ,容量:10,切片值:[0 0 0 1 2 3]

底层数组发生变化,而容量也发生了扩容

    根据以上代码运行结果,可以总结规律如下所示:

  • 增加元素时,当前长度+新增个数 <= cap 时,则不扩容,还是共用先前的底层数组
  • 增加元素时,当前长度+新增个数 > cap 时,发生扩容,生成新的底层数组,并将旧的元素复制到新数组,再追加新的元素

扩容策略:当扩容后的cap<256时,则扩容时,容量翻倍,当cap>=256时,则newCap=newCap+newCap/4+192,即1.25倍后,再加192

    在熟悉了切片的长度和容量后,我们再来看看切片的截取的注意事项,示例代码如下所示:

package main

import "fmt"

func main() {
    s1 := []int{10, 20, 30, 40, 50}
    fmt.Printf("切片s1内存地址:%p 底层数组首地址:%p,第2个元素地址:%p 切片长度:%d 容量: %d 切片值:%v\n", &s1, &s1[0], &s1[1], len(s1), cap(s1), s1)
    // s1和s2共用底层数组
    s2 := s1
    fmt.Printf("切片s2内存地址:%p 底层数组首地址:%p,第2个元素地址:%p 切片长度:%d 容量: %d 切片值:%v\n", &s2, &s2[0], &s1[1], len(s2), cap(s2), s2)

    // 切片截取,仍共用底层数组
    s3 := s1[:]
    fmt.Printf("切片s3内存地址:%p 底层数组首地址:%p,第2个元素地址:%p  切片长度:%d 容量: %d 切片值:%v\n", &s3, &s3[0], &s1[1], len(s3), cap(s3), s3)

    // 截取第1个元素到末尾,共仍共用一个底层数组,首地址偏移一个地址
    s4 := s1[1:]
    fmt.Printf("切片s4内存地址:%p 底层数组首地址:%p 切片长度:%d 容量: %d 切片值:%v\n", &s4, &s4[0], len(s4), cap(s4), s4)

    // 截取中间元素,仍共用底层元素
    s5 := s1[1:4]
    fmt.Printf("切片s5内存地址:%p 底层数组首地址:%p 切片长度:%d 容量: %d 切片值:%v\n", &s5, &s5[0], len(s5), cap(s5), s5)

    // 截取开始到结尾的所有元素
    s6 := s1[:4]
    fmt.Printf("切片s6内存地址:%p 底层数组首地址:%p 切片长度:%d 容量: %d 切片值:%v\n", &s6, &s6[0], len(s6), cap(s6), s6)

    // 截取空元素
    s7 := s1[1:1]
    fmt.Printf("切片s7内存地址:%p 切片长度:%d 容量: %d 切片值:%v\n", &s7, len(s7), cap(s7), s7)

    s8 := s1[4:4]
    fmt.Printf("切片s8内存地址:%p 切片长度:%d 容量: %d 切片值:%v\n", &s8, len(s8), cap(s8), s8)

    s9 := s1[5:5]
    fmt.Printf("切片s9内存地址:%p 切片长度:%d 容量: %d 切片值:%v\n", &s9, len(s9), cap(s9), s9)

    s9 = append(s9, 60)
    fmt.Printf("切片s9内存地址:%p 底层数组首地址:%p 切片长度:%d 容量: %d 切片值:%v\n", &s9, &s9[0], len(s9), cap(s9), s9)
}

    运行结果如下所示:

切片s1内存地址:0xc0000a4030 底层数组首地址:0xc0000b8030,第2个元素地址:0xc0000b8038 切片长度:5 容量: 5 切片值:[10 20 30 40 50]
切片s2内存地址:0xc0000a4060 底层数组首地址:0xc0000b8030,第2个元素地址:0xc0000b8038 切片长度:5 容量: 5 切片值:[10 20 30 40 50]
切片s3内存地址:0xc0000a4090 底层数组首地址:0xc0000b8030,第2个元素地址:0xc0000b8038  切片长度:5 容量: 5 切片值:[10 20 30 40 50]
切片s4内存地址:0xc0000a40c0 底层数组首地址:0xc0000b8038 切片长度:4 容量: 4 切片值:[20 30 40 50]
切片s5内存地址:0xc0000a40f0 底层数组首地址:0xc0000b8038 切片长度:3 容量: 4 切片值:[20 30 40]
切片s6内存地址:0xc0000a4120 底层数组首地址:0xc0000b8030 切片长度:4 容量: 5 切片值:[10 20 30 40]
切片s7内存地址:0xc0000a4150 切片长度:0 容量: 4 切片值:[]
切片s8内存地址:0xc0000a4180 切片长度:0 容量: 1 切片值:[]
切片s9内存地址:0xc0000a41b0 切片长度:0 容量: 0 切片值:[]
切片s9内存地址:0xc0000a41b0 底层数组首地址:0xc0000a6158 切片长度:1 容量: 1 切片值:[60]

    通过以上代码运行结果可以总结如下所示:

  • startIndex默认为0,endIndex默认为切片长度
  • 通过指针确定底层数组从哪里开始共享
  • 长度=endIndex-startIndex
  • 容量是底层数组从偏移的元素到结尾还有几个元素

    以上代码运行图解如下所示:

0602-切片截取长度-容量变化示意图.png

6.3 Map

    映射Map,是一种无序键值对的集合,其主要特点如下所示:

  • 长度可变
  • 存储的元素是key-value,其中key是可hash值,value可变
  • key无序不重复
  • 不可索引,需要通过key来访问
  • 不支持零值可用,即,必须使用make或*字面常量8构造
  • 属于引用类型

6.3.1 Map定义

    其定义语法如下所示:

// 仅定义一种约束条件,即仅定义一种类型
var name map[key]valueType

// 定义并赋值
var name = map[key]valueType{key:value}

var name =map[key]valueType{}
name[key]=value

// 使用make定义
name:=make(map[key]valueType)

    示例代码如下所示:

package main

import "fmt"

func main() {
    // 仅定义一种约束
    var m1 map[int]string

    // 定义并赋值
    var m2 = map[int]string{1: "m2"}
    var m3 = map[int]string{}
    m3[1] = "m3"
    // 使用make定义
    m4 := make(map[int]string)
    m4[1] = "m4"

    fmt.Printf("m1:%+v\n", m1)
    fmt.Printf("m2:%+v\n", m2)
    fmt.Printf("m3:%+v\n", m3)
    fmt.Printf("m4:%+v\n", m4)
}

    代码运行结果如下所示:

m1:map[]
m2:map[1:m2]
m3:map[1:m3]
m4:map[1:m4]

6.3.2 Map 新增或修改

    在Map可以添加和修改key对应的值,注意事项:

  • key不存在,则创建新的key和value
  • key存在,则覆盖其value值

    示例代码如下所示:

package main

import "fmt"

func main() {
    var m = make(map[string]string)
    m["a"] = "a"
    fmt.Printf("m value:%+v\n", m)
    m["b"] = "b"
    m["a"] = "aa"
    fmt.Printf("m value:%+v\n", m)
}

    运行结果如下所示:

m value:map[a:a]
m value:map[a:aa b:b]

6.3.3 Map查找

    在Map里面一般使用key进行查找,注意事项如下:

  • 若key存在,则返回相应的值
  • 若key不存在,则返回零值,会导致无法判断key是否存在,需要解析返回值
if _,ok:=map[key];ok{

}

    示意代码如下所示:

package main

import "fmt"

func main() {
    personInfo := map[string]string{
        "name":     "Surpass",
        "age":      "28",
        "location": "Shanghai",
    }
    existKey := "name"
    notExistKey := "abc"
    // key存在
    fmt.Printf("key %s 存在,对应的值:%+v\n", existKey, personInfo[existKey])
    // key不存在
    fmt.Printf("key %s 不存在,对应的值:%+v\n", notExistKey, personInfo[notExistKey])
    // 解析返回值,来判断key是否存在
    if _, ok := personInfo[notExistKey]; !ok {
        fmt.Printf("key %s 不存在\n", notExistKey)
    }
    if _, ok := personInfo[existKey]; ok {
        fmt.Printf("key %s 存在,对应的值为:%s\n", existKey, personInfo[existKey])
    }
}

    运行结果如下所示:

key name 存在,对应的值:Surpass
key abc 存在,对应的值:
key abc 不存在
key name 存在,对应的值为:Surpass

6.3.4 Map 删除

    删除Map中的元素,比较简单,基本格式如下所示:

delete(map,key)
  • 若key存在,则删除相应的key-value对
  • 若key不存在,也不会出现panic

    示意代码如下所示:

package main

import "fmt"

func main() {
    personInfo := map[string]string{
        "name":     "Surpass",
        "age":      "28",
        "location": "Shanghai",
    }
    existKey := "name"
    notExistKey := "abc"
    delete(personInfo,existKey)
    delete(personInfo,notExistKey)
    fmt.Printf("删除后的map值:%+v\n", personInfo)
}

    代码运行结果如下所示:

删除后的map值:map[age:28 location:Shanghai]

6.3.5 遍历

    map的遍历使用for-range,基本语法格式如下所示:

for k,v:=range map{
    fmt.Println(k,v)
}

    示例代码如下所示:

package main

import "fmt"

func main() {
    personInfo := map[string]string{
        "name":     "Surpass",
        "age":      "28",
        "location": "Shanghai",
    }
    for k,v:=range personInfo{
        fmt.Printf("key:%s,value:%s\n",k,v)
    }
}

    运行结果如下所示:

key:location,value:Shanghai
key:name,value:Surpass
key:age,value:28
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容