数组的特点:
- 数组的长度在定义之后无法再次修改;
- 数组是值类型,每次传递都将产生一份副本。
显然这种数据结构无法完全满足开发者的真实需求。
Go语言提供了数组切片(slice)这个非常酷的功能来弥补数组的不足。
数组切片就像一个指向数组的指针,实际上它拥有自己的数据结构,而不仅仅是个指针。
数组切片的数据结构可以抽象为以下3个变量:
- 一个指向原生数组的指针;
- 数组切片中的元素个数;
- 数组切片已分配的存储空间。
数据切片 和 数组 之间的关系 类似于 C++中的std::vector和数组
数组切片添加了一系列管理功能,可以随时动态扩充存放空间,
并且可以被随意传递而不会导致所管理的元素被重复复制。
package mainimport "fmt"func main() {var myArray [10] int = [10]int {1,2,3,4,5,6,7,8,9,10}var mySlice []int = myArray[:5]for _, v := range myArray {fmt.Print(v, " ")}fmt.Println()for _, v := range mySlice {fmt.Print(v, " ")}}
Go语言提供的内置函数 make() 可以用于灵活地创建数组切片。
# 创建一个初始元素个数为5的数组切片,元素初始值为0:mySlice1 := make([]int, 5)创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间:mySlice2 := make([]int, 5, 10)直接创建并初始化包含5个元素的数组切片:mySlice3 := []int{1, 2, 3, 4, 5}
注: [10]int 算是数组, []int 才是切片
可动态增减元素是数组切片比数组更为强大的功能。与数组相比,数组切片多了一个存储能力(capacity)的概念,即元素个数和分配的空间可以是两个不同的值。
package mainimport "fmt"func main() {mySlice := make([]int, 5, 10)fmt.Println(len(mySlice)) // 切片的长度5fmt.Println(cap(mySlice)) // 切片的容量10fmt.Println(mySlice) // [0 0 0 0 0]mySlice = append(mySlice, 1, 2, 3) // 往切片后追加fmt.Println(mySlice) // [0 0 0 0 0 1 2 3]mySlice2 := []int{8, 9, 10}mySlice = append(mySlice, mySlice2...) // 加上省略号相当于把切片解包fmt.Println(mySlice) // [0 0 0 0 0 1 2 3 8 9 10]}
数组切片支持Go语言的另一个内置函数 copy() ,用于将内容从一个数组切片复制到另一个数组切片。如果加入的两个数组切片不一样大,就会按其中较小的那个数组切片的元素个数进行复制。
// 注: copy(dst, src)slice1 := []int{1, 2, 3, 4, 5}slice2 := []int{5, 4, 3}copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置
func main() {a := []int{7, 8, 9}fmt.Printf("%+v\n", a)ap(a)fmt.Printf("%+v\n", a)app(a)fmt.Printf("%+v\n", a)}func ap(a []int) {a = append(a, 10)}func app(a []int) {a[0] = 1}
[7 8 9] [7 8 9] [1 8 9]
因为append导致底层数组重新分配内存了,append中的a这个slice的底层数组和外面不是一个,并没有改变外面的。
