常用python的同学应该对切片并不陌生。切片是对数组的一种扩展操作。在Go语言中,数组是定长的,引入切片后可以追加元素、删除元素。使得对数组的操作更加灵活。
切片就类似于数组的一个视图(view),对切片的操作可以 直接映射到数组上。
还记得我们上节学到的指针吗?由于go语言无法 进行引用传递。我们在传递数组参数时采用了指针传法。现在我们学了切片之后进行这样的改进
func Slice(arr []int) {
arr[0] = 123
for i,v:= range arr{
fmt.Println(i,v)
}
}
这样我们对某个数组的操作就可以在函数内改变后,作用到主函数中了。
//main函数中
Slice(arr3[:])
fmt.Println(arr3)
那么在go语言中切片到底是如何定义与操作的呢?
1、数组映射出一个切片
我们前面说,切片是数组的视图。我们可以通过数组定义出一个切片
arr := [...]int{0,1,2,3,4,5,6,7}
s := arr[2:6]
fmt.Println(s)//半开半闭区间
这里的s就是一个切片。表示[2,6)的左闭右开区间。所以切片主要是有这四种数组映射方式。
fmt.Println("arr[2:6] = ",arr[2:6])//区间[2,6)
fmt.Println("arr[:6] = ",arr[:6])//区间[0,6)
fmt.Println("arr[2:] = ",arr[2:])//区间[2,len(arr)]
fmt.Println("arr[:] = ",arr[:])//区间[0,len(arr)]
2、直接定义切片
切片也可进行单独定义,切片的内部结构如下
var s []int//zero value of slice is nil
这里就定义了一个空切片。或者根据上面的切片架构利用make定义
s2 := make([]int,16)
s3 := make([]int,10,32)
其中len指定的是已经开辟的空间长度,cap表示可扩展空间的长度。
1、切片的扩展
arr2 := [...]int{0,1,2,3,4,5,6,7}
si1 := arr2[2:6]
si2 := si1[3:5]//[si1[3],si1[4]]
//slice可以向后扩展,可以向后扩展
//s[i]不可以超越len(s),向后扩展不可以超越底层数组cap(s)
fmt.Println(si1,si2)//[2 3 4 5] [5 6]
在上面的图和代码中,可以看出si1,si2均为数组arr2的视图,当slice长度不够时,会自己向后扩展,但不会超过底层数组arr2的长度。
2、切片的追加
arr := [...]int{0,1,2,3,4,5,6,7}
s2 := arr[:]
s3 := append(s2,10)
s4 := append(s3,11)
s5 := append(s4,12)
fmt.Println("s2,s3,s5 = ",s3,s4,s5)
//s4 和 s5不再是对arr的一个view。系统自动分配相应的array
//添加元素超越cap,系统会重新分配更大的底层数组
//由于值传递,必须接收append的返回值
在对切片进行追加时,一旦超出了底层数组的长度,则系统自动复制并扩容出新的底层数组。所以上述代码中的s4,s5不再是arr的view了,而是新的底层数组的视图。
3、切片的复制
fmt.Println("copying slice")
copy(s2,s1)
切片可进行复制操作
4、切片的元素删除。由于切片没有直接的delete方法,所以可以通过拼接来实现删除
fmt.Println("deleting elements from slice")
s2 = append(s2[:3],s2[4:]...)
printSlice(s2)
fmt.Println("poping from front")
front := s2[0]
s2 = s2[1:]
fmt.Println("poping from back")
tail := s2[len(s2)-1]
s2 = s2[:len(s2)-1]
fmt.Println(front,tail)
printSlice(s2)
删除中间某个元素即获取到该元素的下标后,将此元素前后的值拼接起来实现删除该元素的目的。
s2 = append(s2[:3],s2[4:]...)中的...可以实现多元素的逐个追加
删除头尾元素如代码所示,不再赘述。