Go学习笔记02-数组|Slice

数组

数组是同一类型元素的集合,Go语言中数组不允许混合不同类型的元素,例如包含字符串和整数的数组(除非是interface{}类型的数组,那么就可以包含任意类型)。

数组的声明

一个数组的表现形式为[n]T。n表示数组中元素的数量,T代表每个元素的类型。注意:元素的数量n也是该类型的一部分,因此[5]int[25]int是不同的类型。数组声明后不能再调整大小。

声明数组的几种方式:

  • 方式1:var a [3]int,声明了一个长度为3的整型数组,数组中的所有元素都被自动赋值为数组类型的零值;
  • 方式2:a := [3]int{12, 34, 56},这里使用了简略声明,在简略声明中,不需要将数组中所有的元素赋值,例如a := [3]int{12},这里声明了一个长度为3的数组,但只提供了一个值12,剩下的2个元素自动赋值为0;
  • 方式3:a := [...]int{12, 34, 56},这里忽略声明数组的长度,并用...代替,编译器会自动计算长度。

数组是值类型

Go中的数组是值类型而不是引用类型。这意味着:

  • 当数组赋值给一个新变量时,该变量会得到原始数组的一个副本,如果对新变量进行更改,则不会影响原始数组;
  • 同样,当数组作为参数传递给函数时,它们是按值传递,而原始数组保持不变。

数组的遍历

假设有这样一个数组:a := [...]float64{67.7, 89.9, 21, 78},传统的遍历方法为:

for i := 0; i < len(a); i++ {
    fmt.Printf("%d the element of a is %.2f\n", i, a[i])
}

Go提供了一种更好、更简洁的方法:使用range迭代数组,range返回索引和该索引处的值,如果只需要索引或值,可以通过空白标识符_来忽略其中一个。

for i, v := range a {
    fmt.Printf("%d the element of a is %.2f\n", i, v)
}

切片(Slice)

切片是由数组建立的一种方便、灵活且功能强大的包装。切片本身不拥有任何数据,它们只是对现有数组的引用。带有T类型元素的切片由[]T表示。

切片的创建

创建Slice的几种方式:

  • 使用语法a[start:end]创建一个从a数组索引start开始到end-1结束的切片;
  • s := []int{6, 7, 8}创建一个有3个整型元素的数组,并返回一个存储在s中的切片引用;
  • 使用make创建一个切片:func make([]T, len, cap) []T。其中容量cap选项是可选参数,默认值为切片长度。make函数创建一个数组,并返回引用该数组的切片。

切片的修改

切片自己不拥有任何数据。它只是底层数组的一种表示。对切片所做的任何修改都会反应在底层数组中。当多个切片共用相同的底层数组时,每个切片所做的更改将反映在数组中。

切片的长度和容量

切片的长度是切片中的元素数,切片的容量是从创建切片索引开始的底层数组中元素数。

func main() {
    ary := [...]int{11, 22, 33, 44, 55, 66, 77}
    slice := ary[1:3]
    // Output: length of slice 2 capacity 6
    fmt.Printf("length of slice %d capacity %d\n", len(slice), cap(slice))
}

追加切片元素

使用append函数可以将新元素追加到切片上,append函数的定义是func append(s []T, x ...T) []T。切片类型的零值为nil。一个nil切片的长度和容量为0。可以使用append函数将值追加到nil切片:

var names []string
names = append(names, "John", "Vinay", "Michael")

也可以使用...运算符将一个切片添加到另一个切片:

a := []int{1, 2, 3, 4}
b := []int{5, 6, 7}
s := append(a, b...)

切片的函数传递

可以认为,切片在内部由一个结构体类型表示,包含长度、容量和指向数组第零个元素的指针。当切片传递给函数时,即使它通过值传递,指针变量也将引用相同的底层数组。因此,当切片作为参数传递给函数时,函数内所做的更改也会在函数外可见。

内存优化

切片持有对底层数组的引用。只要切片在内存中,数组就不能被垃圾回收。在内存管理方面,这是需要注意的。 假设有一个非常大的数组,但我们只想处理它的一小部分,一种好的方式是创建一个新的slice,然后使用copy函数来生成一个待处理切片的副本,这样原始数组就可以被垃圾回收了。

reference:

https://studygolang.com/articles/12121