在实际开发中,大多数情况处理的数据都不是单一的,而是复数的同类型数据。所有的高级语言都会封装相应的数据结构、API等。在Go语言中,提供了数组、切片以及映射三种数据结构供开发者管理和处理集合数据。切片基于数组,却有别于数组,本次笔记学习Go切片相关的内容。
切片结构
为了更方便学习切片的内容,首先简单了解一下切片的底层长什么样。

创建切片
使用内置数据结构的第一步:声明创建初始化。 切片有三种创建方式 使用make创建切片
//语法: make([]切片类型, 长度, 容量) 其中容量为可选值,容量>=长度
//创建一个长度为5,容量为10的整形切片
intSlice := make([]int, 5, 10)
//如果不指定容量值,则生成长度与容量相等的切片
//创建一个长度为10,容量为10的字符串切片
stringSlice := make([]string, 10)
PS: make函数也可用于创建map及channel,若有写到相关内容再细聊。
访问与修改切片元素与数组一致
intSlice[2] = 3
stringSlice[4] = "ji ni tai mei"
//未赋值的元素为默认零值
/*
如果访问的索引超过切片长度,编译会产生
index out of range越界异常,如
fmt.Println(intSlice[5])
*/
声明并初始化
//语法: []类型{初始化元素列表, ...}
//创建一个长度为4,容量为4的字符串切片
stringSlice := []string{"red", "yellow", "blue", "green"}
//创建一个空切片
nilSlice := []int{}
请注意此方式与创建数组的区别,当中括号有具体数值时表示创建数组。
//创建一个有初始化元素的字符串数组
stringArr := [5]string{"red", "yellow", "blue", "green"}
也可以指定长度。
//创建长度和容量为10的字符串切片
stringSlice := []string{9: "rainbow"}
从一个切片中创建切片
slice := []int{1, 2, 3, 4, 5, 6}
//语法: 原切片[i:j:容量] //容量为可选,不指定时标识到原切片末尾
newSlice := slice[1:4] //newSlice为 [2, 3, 4]
以上创建了一个长度为3,容量为5的切片。注意此处切片的长度为j-i,j不是结束元素的索引。
前面了解了切片的底层,用此种方式创建的切片的会共享底层的数组,可以理解为,“从切片中切片”,那么修改切片会相互受到影响。
slice[2] = 99
fmt.Println(newSlice[1]) //结果输出99
从切片中切片↓

切片扩容
相较于数组,切片的一个优势是可以动态增长。通过append()增长切片。
在此之前,了解一下两个内置函数len()和cap(),前者返回切片的长度,后者返回切片的容量。
//写个简单的函数输出切片长度、容量和内容
func echoStringSlice(slice []string) {
fmt.Printf("len: %d\n", len(slice))
fmt.Printf("cap: %d\n", cap(slice))
fmt.Println(slice)
}
//语法: append(原切片, ...新增原始) //第二参数为可变参数,可以添加任意多个
func main() {
stringSlice := []string{"red", "yellow", "blue", "green"}
echoStringSlice(stringSlice)
fmt.Println("After append...")
newSlice := append(stringSlice, "rainbow")
echoStringSlice(newSlice)
}
编译运行后,结果为
len: 4
cap: 4
[red yellow blue green]
After append...
len: 5
cap: 8
[red yellow blue green rainbow]
通过append()函数会新生成一个切片,新生成的切片长度增长了,当切片的容量足够添加新的元素时,容量并不会发生改变,仅仅只是切片的长度变化,而当容量不足时则会动态增长底层数组,新生成的切片容量为原来的两倍(原容量小于1024时, 否则按增长因子0.25增长相应容量,随着数值的上升,增长因子可能会变得更小)
函数间传递切片
在函数间传递切片的空间开销是很小的。前面简单了解了切片的底层结构,那么在函数间传递切片时,切片在传递时大概只有一个指针、两个整形加起来那么大。而函数间传递数组时,需要复制整一个数组,对各方面的开销都是比较巨大的,当然也可以用指针来传递,但对指针操作稍不注意可能就会出现安全问题,且想要动态增长也没那么方便了。