一、数组Array

Go语言数组是一个定长的序列,其内部元素一般限定同一类型。数组为切片的底层结构。

  1. //创建数组1:声明后赋值
  2. var arr1 [3]int
  3. arr1[0] = 0
  4. arr1[1] = 1
  5. arr1[2] = 2
  6. //创建数组2:创建并初始化
  7. arr2 := [3]int{1, 3, 5}
  8. //创建数组3:自动计算数组长度
  9. arr3 := [...]int{2, 4, 6, 8, 10}
  10. //len()为内置函数,用来获取数组、切片、字符串、通道(channel)等的长度
  11. var count = len(arr3)
  12. fmt.Printf("类型为%T,值为%v,元素个数为%d\n", arr3, arr3, count)
  13. //类型为[6]int,值为[1 2 3 4 5 6],元素个数为6
  14. //声明一个两行三列的二维数组
  15. var grid [2][3]int
  16. grid[0] = [3]int{1,3,5}
  17. grid[1] = [3]int{2,4,6}
  18. fmt.Println(grid) //[[1 3 5] [2 4 6]]
  19. //遍历数组
  20. for index, value := range arr1 {
  21. fmt.Println(index, " ", value)
  22. }
  23. //0 0
  24. //1 1
  25. //2 2
  26. //3 3
  27. //4 4

二、切片Slice

1.切片声明及初始化
  1. //array[start:end]从数组身上截取下标为[start,end)片段,形成切片
  2. //start代表开始下标,不写默认代表从头开始切
  3. //end代表结束下标(本身不被包含),不写默认截取到末尾
  4. func BaseSlice01() {
  5. //声明并初始化一个数组
  6. var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
  7. //基于数组创建切片
  8. slice1 := arr[2:5]
  9. slice2 := arr[:5]
  10. slice3 := arr[6:]
  11. slice4 := arr[:]
  12. fmt.Printf("arr类型为%T,值为%v\n", arr, arr) //arr类型为[8]int,值为[0 1 2 3 4 5 6 7]
  13. fmt.Printf("slice1类型为%T,值为%v\n", slice1, slice1) //slice1类型为[]int,值为[2 3 4]
  14. fmt.Printf("slice2类型为%T,值为%v\n", slice2, slice2) //slice2类型为[]int,值为[0 1 2 3 4]
  15. fmt.Printf("slice3类型为%T,值为%v\n", slice3, slice3) //slice3类型为[]int,值为[6 7]
  16. fmt.Printf("slice4类型为%T,值为%v\n", slice4, slice4) //slice4类型为[]int,值为[0 1 2 3 4 5 6 7]
  17. }

2.切片追加元素
  1. //向切片中追加元素
  2. func BaseSlice02() {
  3. slice := []int{1, 2, 3, 4}
  4. slice = append(slice, 5, 6) //直接追加多个元素
  5. slice = append(slice, []int{7, 8, 9}...) //追加另一个切片里的元素,需将被追加元素“拍扁”,后面加...即可
  6. fmt.Printf("slice1类型为%T,值为%v,长度为%d,容量为%d\n", slice, slice, len(slice), cap(slice))
  7. ////slice1类型为[]int,值为[1 2 3 4 5 6 7 8 9],长度为9,容量为16
  8. //遍历切片
  9. for _, v := range slice {
  10. fmt.Print(v, " ")
  11. }
  12. //1 2 3 4 5 6 7 8 9
  13. }

3.切片容量扩展
  1. //cap(slice)获得切片的容量
  2. //创建之初,容量等于长度
  3. //扩张时,一旦容量无法满足需要,就以翻倍的策略扩容
  4. //注意切片扩容时其栈地址的变化
  5. func BaseSlice03() {
  6. arr := [4]int{1, 2, 3, 4}
  7. fmt.Printf("arr类型为%T,值为%v,长度为%d,容量为%d,地址为%p\n", arr, arr, len(arr), cap(arr), &arr)
  8. slice := arr[:]
  9. fmt.Printf("slice类型为%T,值为%v,长度为%d,容量为%d,地址为%p\n", slice, slice, len(slice), cap(slice), slice)
  10. slice = append(slice, 5)
  11. fmt.Printf("slice类型为%T,值为%v,长度为%d,容量为%d,地址为%p\n", slice, slice, len(slice), cap(slice), slice)
  12. slice = append(slice, 6)
  13. fmt.Printf("slice类型为%T,值为%v,长度为%d,容量为%d,地址为%p\n", slice, slice, len(slice), cap(slice), slice)
  14. slice = append(slice, 7)
  15. fmt.Printf("slice类型为%T,值为%v,长度为%d,容量为%d,地址为%p\n", slice, slice, len(slice), cap(slice), slice)
  16. slice = append(slice, 8)
  17. fmt.Printf("slice类型为%T,值为%v,长度为%d,容量为%d,地址为%p\n", slice, slice, len(slice), cap(slice), slice)
  18. slice = append(slice, 9)
  19. fmt.Printf("slice类型为%T,值为%v,长度为%d,容量为%d,地址为%p\n", slice, slice, len(slice), cap(slice), slice)
  20. slice = append(slice, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)
  21. fmt.Printf("slice类型为%T,值为%v,长度为%d,容量为%d,地址为%p\n", slice, slice, len(slice), cap(slice), slice)
  22. }
  23. //arr类型为[4]int,值为[1 2 3 4],长度为4,容量为4,地址为0xc00011e000
  24. //slice类型为[]int,值为[1 2 3 4],长度为4,容量为4,地址为0xc00011e000
  25. //slice类型为[]int,值为[1 2 3 4 5],长度为5,容量为8,地址为0xc000126040
  26. //slice类型为[]int,值为[1 2 3 4 5 6],长度为6,容量为8,地址为0xc000126040
  27. //slice类型为[]int,值为[1 2 3 4 5 6 7],长度为7,容量为8,地址为0xc000126040
  28. //slice类型为[]int,值为[1 2 3 4 5 6 7 8],长度为8,容量为8,地址为0xc000126040
  29. //slice类型为[]int,值为[1 2 3 4 5 6 7 8 9],长度为9,容量为16,地址为0xc0000c0180
  30. //slice类型为[]int,值为[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20],长度为20,容量为32,地址为0xc00012c000
  31. //每次切片扩容都是容量倍增,且其切片头指针地址都发生变化,可见倍增时是另开辟内存并复制

4.切片兼并
  1. /*兼并另一个切片*/
  2. func BaseSlice04() {
  3. slice := []int{1, 2, 3, 4}
  4. fmt.Printf("slice1类型为%T,值为%v,长度为%d,容量为%d\n", slice, slice, len(slice), cap(slice))
  5. sli := []int{7, 8, 9}
  6. slice = append(slice, sli...) //兼并时注意“拍扁”
  7. fmt.Printf("slice1类型为%T,值为%v,长度为%d,容量为%d\n", slice, slice, len(slice), cap(slice))
  8. }
  9. //slice1类型为[]int,值为[1 2 3 4],长度为4,容量为4
  10. //slice1类型为[]int,值为[1 2 3 4 7 8 9],长度为7,容量为8

5.使用make()函数创建切片
  1. /*创建指定长度和容量的切片*/
  2. func BaseSlice05() {
  3. //创建空切片
  4. slice1 := make([]int, 0)
  5. fmt.Printf("slice1类型为%T,值为%v,长度为%d,容量为%d\n", slice1, slice1, len(slice1), cap(slice1))
  6. //slice1类型为[]int,值为[],长度为0,容量为0
  7. //创建空切片,但指定容量
  8. slice2 := make([]int, 0, 5)
  9. fmt.Printf("slice2类型为%T,值为%v,长度为%d,容量为%d\n", slice2, slice2, len(slice2), cap(slice2))
  10. //slice2类型为[]int,值为[],长度为0,容量为5
  11. //创建长度为3容量为5的切片,默认长度的元素都初始化为零值
  12. slice3 := make([]int, 3, 5)
  13. fmt.Printf("slice3类型为%T,值为%v,长度为%d,容量为%d\n", slice3, slice3, len(slice3), cap(slice3))
  14. //slice3类型为[]int,值为[0 0 0],长度为3,容量为5
  15. //创建长度为5的切片,默认长度的元素都初始化为零值,未指定容量时容量值等于长度值
  16. slice4 := make([]int, 5)
  17. fmt.Printf("slice4类型为%T,值为%v,长度为%d,容量为%d\n", slice4, slice4, len(slice4), cap(slice4))
  18. //slice4类型为[]int,值为[0 0 0 0 0],长度为5,容量为5
  19. }

6.删除切片元素

go没有内置的切片删除函数,所以删除元素需要遍历切片

  1. //删除切片元素
  2. func RmSliceElement(slice []interface{}, index int) []interface{} {
  3. if index > len(slice)-1 {
  4. panic("index out of range")
  5. }
  6. for _, v := range slice {
  7. //如果是最后一个元素
  8. if index == len(slice)-1 {
  9. return slice[:index]
  10. }
  11. //如果是中间元素
  12. if v == slice[index] {
  13. return append(slice[:index], slice[index+1:]...)
  14. }
  15. }
  16. return nil
  17. }

三、映射Map

1.map的创建
  1. //创建并初始化
  2. m1 := map[string]string{
  3. "name": "fun",
  4. "age": "18",
  5. "gender": "male",
  6. }
  7. //创建一个长度为5的空map,已分配内存
  8. m2 := make(map[string]int,5)
  9. //只声明一个map,未分配内存
  10. var m3 map[string]int
  11. fmt.Printf("m1:%v,地址为%p\n", m1,m1)
  12. fmt.Printf("m2:%v,地址为%p\n", m2,m2)
  13. fmt.Printf("m3:%v,地址为%p\n", m3,m3)
  14. //m1:map[age:18 gender:male name:fun],地址为0xc00008e480
  15. //m2:map[],地址为0xc00008e4b0
  16. //m3:map[],地址为0x0

2.map的遍历及访问值
  1. //遍历
  2. m := map[string]string{
  3. "name": "fun",
  4. "age": "18",
  5. "gender": "male",
  6. }
  7. for key, val := range m {
  8. fmt.Printf("Key:%s,Value:%v\n", key, val)
  9. }
  10. //Key:name,Value:fun
  11. //Key:age,Value:18
  12. //Key:gender,Value:male
  13. //访问一个键值
  14. name := m["name"]
  15. fmt.Printf("name:%v\n", name)
  16. //name:fun
  17. //访问一个不存在的键
  18. names := m["names"]
  19. fmt.Printf("names:%v\n", names)
  20. //names:
  21. //带校验的访问
  22. if a, ok := m["name"]; !ok {
  23. fmt.Println("key is not exist!")
  24. } else {
  25. fmt.Println(a)
  26. }

3.map删除元素

delete()为内置函数,只对map有效

  1. func BaseMap03() {
  2. m := map[string]string{
  3. "name": "fun",
  4. "age": "18",
  5. "gender": "male",
  6. }
  7. fmt.Printf("m:%v\n", m)
  8. delete(m, "age")
  9. fmt.Printf("m:%v\n", m)
  10. }
  11. //m:map[age:18 gender:male name:fun]
  12. //m:map[gender:male name:fun]

4.多维map的使用
  1. //多维map的使用
  2. func BaseMap04() {
  3. //声明二维map的结构
  4. var ms = make(map[string]map[string]int, 3)
  5. fmt.Printf("ms:Type:%T, Value:%v, Len:%d, Address:%p\n", ms, ms, len(ms), ms)
  6. //为二维map的内部初始化
  7. ms["a"] = make(map[string]int, 4)
  8. ms["b"] = make(map[string]int, 5)
  9. ms["c"] = make(map[string]int, 6)
  10. fmt.Printf("ms:Type:%T, Value:%v, Len:%d, Address:%p\n", ms, ms, len(ms), ms)
  11. ms["d"] = make(map[string]int, 7)
  12. fmt.Printf("ms:Type:%T, Value:%v, Len:%d, Address:%p\n", ms, ms, len(ms), ms)
  13. //赋值:只有二维数组内部初始化后才能赋值
  14. //否则报panic: assignment to entry in nil map
  15. ms["a"]["aa"] = 1
  16. ms["a"]["bb"] = 2
  17. ms["a"]["cc"] = 3
  18. fmt.Printf("ms:Type:%T, Value:%v, Len:%d, Address:%p\n", ms, ms, len(ms), ms)
  19. //[1]ms:Type:map[string]map[string]int, Value:map[], Len:0, Address:0xc000104300
  20. //[2]ms:Type:map[string]map[string]int, Value:map[a:map[] b:map[] c:map[]], Len:3, Address:0xc000104300
  21. //[3]ms:Type:map[string]map[string]int, Value:map[a:map[] b:map[] c:map[] d:map[]], Len:4, Address:0xc000104300
  22. //[4]ms:Type:map[string]map[string]int, Value:map[a:map[aa:1 bb:2 cc:3] b:map[] c:map[] d:map[]], Len:4, Address:0xc000104300
  23. }

四、关于make()和new()函数

1.make()

Go为创建切片slice、映射map、通道chan内建一个函数,即make()。

用法:

  1. func make(Type, size IntegerType) Type

根据官方文档:

  • 参数一为类型,非值,返回类型与其参数相同
  • 参数二不同类型传参不同

切片:size指定了其长度。该切片的容量等于其长度。切片支持第二个整数实参可用来指定不同的容量;它必须不小于其长度,因此 make([]int, 0, 10) 会分配一个长度为0,容量为10的切片。

映射:初始分配的创建取决于size,但产生的映射长度为0。size可以省略,这种情况下就会分配一个小的起始大小。

通道:通道的缓存根据指定的缓存容量初始化。若 size为零或被省略,该信道即为无缓存的。

2.new()

用法:

  1. func new(Type) *Type

内建函数new分配内存。其第一个实参为类型,而非值。其返回值为指向该类型的新分配的零值的指针。

3.两个函数的区别
  • make()是专门用来创建切片、映射、通道的,为其分配内存并初始化零值,返回值而非指针。
  • new()是为任意类型分配内存的,但不会初始化零值,只会把内存置零,返回的是*T指针。一般用于创建array或struct值类型。