切片简介
Go的切片(slice)是Go语言内置的一种数据结构。
在使用上,Go的切片和C++ STL的vector有以下相同点:
- 都可以直接索引,像数组一样
- 随着元素个数的增加,可以自动扩大容量。(每超过一次,则将容量扩展为当前的两倍(Go目前的版本就是两倍扩展,而STL vector没有明确规定,根据不同的实现版本,2倍或者1.几(隐约记得visual studio2015似乎就是1.几)都有)大小,并自动将原内存的数据拷贝到新内存中)
具体语法
1.声明
1.1 用"make"声明切片
mslice := make([]int, 5, 10) // 创建一个元素类型为int的切片。元素值默认初始化为0
fmt.Println(mslice) // 输出:[0 0 0 0 0]
fmt.Println("len:", len(mslice))//输出: len: 5
fmt.Println("cap:", cap(mslice))//输出: cap: 10
可见,用make创建切片时,第二个参数指定了切片的长度(len),第三个参数指定了切片的容量(cap)。
长度:切片当前有多少个元素
容量:切片最多可以容纳多少个元素。
1.2 用"[]"声明切片
[]type用于声明切片,[…]type用于声明数组。
arr := [...]int{1, 2, 3, 4} //[...]int用于声明一个int类型的数组,数组长度由{}中的元素个数指定
slice_a := []int{1,2,3,4} //slice_a是一个切片
var slice_b []int = arr[:] //slice_b也是一个切片,并且切片的底层数组就是arr。即,修改arr的元素值,会导致slice_a同步改变。
arr[0] = 9 //会导致slice_b[0]也变成9
注意用数组arr初始化切片slice_b 时,相当于将数组arr设为切片slice_b 的底层数组;直到切片slice_b 发生扩展而重新分配底层数组,slice_b 才会与arr脱离联系。
引用元素
mslice[2] = 4
mslice = append(mslice, 8) //在mslice的末尾新增元素8.该操作将导致len+1;如果append之前cap就已经等于len的话,那么append还会导致mslice的cap按两倍扩展
fmt.Println(mslice) //输出:[0 0 4 0 0 8]
fmt.Println(mslice[:]) //[:] 表明mslice的全部元素,输出:[0 0 4 0 0 8]
fmt.Println(mslice[2:]) //[2:] 表明mslice下标为2开始的元素,输出:[4 0 0 8]
fmt.Println(mslice[2:4]) //[2:4] 表明mslice下标为2~3的元素,输出:[4 0]
fmt.Println(mslice[:4]) //[:4] 表明mslice下标为0~3的元素,输出:[0 0 4 0]
fmt.Println("len:", len(mslice))//输出: len: 6
fmt.Println("cap:", cap(mslice))//输出: cap: 10
需要强调的是,像C语言的数组一样,用’[索引]'访问元素时,索引的大小不能超过len-1。
在旧切片的基础上生成新的切片
切片之所以叫做”切片“,就是为了表明它可以很方便的切来切去。
newSlice := mslice[2:4:6] //在mslice的基础上创建新切片newSlice。
有以下三点需要说明:
- mslice[i:j:k]这个语句必须满足一个约束,即i<j<=k<=cap(mslice)。
- 在上面的语句中,newSlice的元素从mslice[2]开始。newSlice的len为4-2=2,cap为6-2=4。mslice[i:j:k],其计算公式为len=j-i,cap=k-i。
- 在newSlice或者mslice的cap扩充前,newSlice依旧使用mslice之前使用的底层数组来存储数据。这表明对newSlice进行修改,会影响mslice!反之亦然。什么时候不再相互影响呢?那就是这两个切片的任意一个的cap扩充时,Go会为了给切片分配新的内存,从这一刻起两个切片在内存上才正式脱离共享关系。