数组的特点:
- 数组的长度在定义之后无法再次修改;
- 数组是值类型,每次传递都将产生一份副本。
显然这种数据结构无法完全满足开发者的真实需求。
Go语言提供了数组切片(slice)这个非常酷的功能来弥补数组的不足。
数组切片就像一个指向数组的指针,实际上它拥有自己的数据结构,而不仅仅是个指针。
数组切片的数据结构可以抽象为以下3个变量:
- 一个指向原生数组的指针;
- 数组切片中的元素个数;
- 数组切片已分配的存储空间。
数据切片 和 数组 之间的关系 类似于 C++中的std::vector和数组
数组切片添加了一系列管理功能,可以随时动态扩充存放空间,
并且可以被随意传递而不会导致所管理的元素被重复复制。
package main
import "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 main
import "fmt"
func main() {
mySlice := make([]int, 5, 10)
fmt.Println(len(mySlice)) // 切片的长度5
fmt.Println(cap(mySlice)) // 切片的容量10
fmt.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的底层数组和外面不是一个,并没有改变外面的。