Go In Action 读书笔记(二)

数组和切片

数组

  • Go中数组是一个长度固定的数据类型,用于存储一段具有相同的类型的元素的连续块。
  • 由于元素类型相同,又是连续分配,是一种高效的数据结构
  • 声明&初始化 go // 声明一个包含 5 个元素的整型数组,此时该数组元素为对应的零值 var array [5]int go // 字面量声明初始化 // 声明一个包含 5 个元素的整型数组 // 用具体值初始化每个元素 array := [5]int{10, 20, 30, 40, 50}
  // 声明一个整型数组
  // 用具体值初始化每个元素
  // 容量由初始化值的数量决定
  array := [...]int{10, 20, 30, 40, 50}
  // 声明一个有 5 个元素的数组
  // 用具体值初始化索引为 1 和 2 的元素
  // 其余元素保持零值
  array := [5]int{1: 10, 2: 20}
  • 数组赋值给另一个数组时,必须确保长度和元素类型都相同才可行

  • 多维数组可以按维度赋值

  • Go的数组和C相同,索引从0开始

  • 给指针数组赋值时,需注意元素是否已经分配内存,若没分配会引发panic

  // 声明一个有 5 个元素的指针数组,1,3两个元素分配了内存,其他均为nil
  array := [5]*int{1:new(int),3:new(int)}
  // 若要对0元素赋值,需先分配内存
  array[0] = new(int)
  *array[0] = 1
  // 下标为1的元素可以直接赋值
  *array[1] = 2
  • 函数之间直接传递数组为值拷贝,函数体内对该数组的修改不会影响原始数组
  • 函数之间传递数组的指针,也是值传递,只不过这个值是指向该数组的地址,所以函数体内对该数组的修改会影响原始数组
  • 区分指针数组和数组的指针

    //指针数组,元素为指针
    var array [5]*int
    //数组的指针,是一个地址,指向数组的指针
    func f(a *[5]int)
    

    切片

  • 切片可以理解为动态的数组,其底层为数组

  • 切片的三要素:指向底层数组的指针,长度,容量,容量>=长度

  • 声明&初始化

  //创建一个字符串切片
  //其长度和容量都是5
  slice := make([]string, 5)
  //创建一个整型切片
  //其长度为3,容量为5   
  slice := make([]int, 3, 5)
  //字面量定义切片
  slice := []string{"Red", "Blue", "Green", "Yellow", "Pink"}
  slice := []int{10, 20, 30}
  //创建字符串切片
  //使用空字符串初始化第100个元素
  slice := []string{99: ""}
  • nil和空切片
  //创建 nil 整型切片
  //三要素:指针为nil,长度和容量均为0
  var slice []int
  // 使用 make 创建空的整型切片
  slice := make([]int, 0)
  // 使用切片字面量创建空的整型切片 
  slice := []int{}
  //三要素:指针不为空,长度和容量均为0,但是底层数组包含0个元素,实际也未分配内存
  • 赋值
  //原始切片,长度为5,容量为5
  slice := []int{1,2,3,4,5}
  //包含两个元素2,3,长度为2,容量为4
  newSlice := slice[1:3]
  //通用公式,slice容量为k
  //newSlice容量为k-i
  //newSlice长度为j-i
  newSlice := slice[i:j]
  //还有一种用来控制新切片的容量,范围[i,j),容量k-i,k小于原始容量
  newSlice := slice[i:j:k]
  • 修改切片,由于实际修改的是底层数组,所以对所有引用该底层数组的切片都有影响
    slice := []int{1,2,3,4,5}
    newSlice := slice[1:3]
    newSlice[1] = 9
    //newSlice的元素为,2,9
    //slice的元素为,1,2,9,4,5
  • 切片只能访问到其长度内的元素。试图访问超出其长度的元素将会导致语言运行时异常,如上述newSlice[3]。与切片的容量相关联的元素只能用于增长切片。在使用这部分元素前,必须将其合并到切片的长度里

  • append

    1. 若切片容量有余,则返回切片仅增加长度,容量不变,即相当于修改底层数组所对应的元素,会影响所有相关切片

    2. 若切片容量已满,则会新建一个底层数组(小于1000时,容量翻倍),返回的切片以该数组为底层数组,修改该数组对应的元素,不会影响原底层数组对应的其他切片

    3. append可以增加单个元素,也可以增加多个元素,还可以增加slice

     slice := []int{1,2,3,4,5}
     newSlice1 := append(slice,6)
     newSlice2 := append(slice,6,7,8)
     newSlice3 := append(slice,newSlice1,newSlice2)
    
  • 切片迭代

    1. for range
     for i,v := range slice{
       fmt.Printf("Index: %d Value: %d Address: %X\n", i, v, &slice[i])
     }
    

    i 为元素索引,v为元素的一个副本

    1. for 循环
     for i := 2; i < len(slice); i++ {
        fmt.Printf("Index: %d Value: %d\n", i, slice[i])
     }
    
    1. 可以用占位符忽略返回值
  • 内置函数 len,cap

len(slice) - 切片的长度

cap(slice) - 切片的容量

  • 切片的传递

切片是一个由三个字段构成的结构体,每个字段各占8个字节,所以在函数间传递 24 字节的数据会非常快速、简单。这也就是说传递的是一个副本切片,共用同一底层函数。

  • 切片与切片不能直接用==比较,唯一能与切片比较的是nil

相关

comments powered by Disqus