Python里面切片是一种操作,用于取list里面元素。而Go语言的切片则是一种数据类型,而不是一种操作。
Go语言中提供了切片(Slice)作为一种更为灵活、功能强悍的内置类型,它其实是数组的一种抽象。
切片的源码:
type slice struct {
array unsafe.Pointer
len int
cap int
}
Slice是原数组在内存中的地址的一个指针,它本身也是一个结构体。Slice建立在数组基础之上,创建一个Slice需要首先创建一个数组。
Go语言的数组同其他语言一样,一经定义,大小就不能改变。这在很多情况下很不方便。因此Go语言内置了切片这种数据类型。切片是数组的一种抽象,但它建立的数组基础之上。与数组相比,切片只保存了原数组的指针、长度、容量等信息,它的长度是不固定的,可以从数组中截取一部分作为切片,也可以动态扩容。
切片声明等价于数组声明不指定数组大小,如下:
var slice_name []type
因此声明数组时一定要指明数组大小,否则会错误的声明成切片。
也可以使用make()
函数来声明切片,并初始化:
var slice_name []type = make([]type, length)
// 可以简写为
slice_name := make([]type, length)
// 也可以指定初始容量
silce_name := make([]type, length, capacity)
切片初始化
Go语言的切片,初始化方法除了make()
函数可以通过类似于数组的花括号,也支持通过对数组进行类Python那样的切片操作得到,非常灵活,但不支持间隔元素截取。
实例如下:
slice := []int {1, 2, 3, 4, 5}
slice := arr[:]
slice := arr[startIndex:endIndex]
startIndex
和endIndex
都是可选的,下同。
也可以通过切片来初始化切片:
s = slice[startIndex:endIndex]
len()和cap()
len()
函数可以获取数组的长度和切片的当前长度。
cap()
函数则可以获取切片的当前能达到的最大长度,即容量。
实例如下:
package main
import "fmt"
func main() {
slice := make([]int, 3, 5)
fmt.Printf("slice = %d slice lengtn=%d slice capacity=%d", slice, len(slice), cap(slice))
}
// slice = [0 0 0] slice lengtn=3 slice capacity=5
该例可以看出slice默认元素值为0。
注:切片在未经初始化前默认为nil
,长度为0。
切片截取
Go语言中的切片支持类Python的元素截取操作,但不支持间隔截取。
语法如下
slice := arr[low:high:max]
package main
import "fmt"
func main() {
arr := []int {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println(arr[2:8])
}
// [3 4 5 6 7 8]
起始索引和结束索引同样可选。
append()和copy
append()
函数用于向已满的切片增加新的元素,可以一次一个或多个元素。
copy
函数则用于复制元素。
实例如下:
package main
import "fmt"
func printSliceInfo(s []int) {
fmt.Printf("current slice=%d length=%d capacity=%d\n", s, len(s), cap(s))
}
func main() {
slice := []int {1, 2, 3}
printSliceInfo(slice)
slice = append(slice, 4)
printSliceInfo(slice)
slice = append(slice, 5, 6, 7, 8, 9, 10)
printSliceInfo(slice)
slice_copy := make([]int, len(slice), (cap(slice)*2))
copy(slice_copy, slice)
printSliceInfo(slice_copy)
}
// current slice=[1 2 3] length=3 capacity=3
// current slice=[1 2 3 4] length=4 capacity=6
// current slice=[1 2 3 4 5 6 7 8 9 10] length=10 capacity=12
// current slice=[1 2 3 4 5 6 7 8 9 10] length=10 capacity=24
注意capacity如何变化。
append
函数仍可向指明了切片max
值的slice中添加元素。给定max
值会影响容量。实例:
package main
import (
"fmt"
)
func printSliceInfo(s []int) {
fmt.Printf("current slice=%d length=%d capacity=%d\n", s, len(s), cap(s))
}
func main() {
arr := [5]int {1, 2, 3, 4, 5}
slice := arr[1:3:4]
printSliceInfo(slice)
slice = append(slice, 6)
printSliceInfo(slice)
slice = append(slice, 7)
printSliceInfo(slice)
}
// current slice=[2 3] length=2 capacity=3
// current slice=[2 3 6] length=3 capacity=3
// current slice=[2 3 6 7] length=4 capacity=6
func main() {
arr := [5]int {1, 2, 3, 4, 5}
slice := arr[1:3:5] // max不能大于数组长度
printSliceInfo(slice)
slice = append(slice, 6)
printSliceInfo(slice)
slice = append(slice, 7)
printSliceInfo(slice)
}
// current slice=[2 3] length=2 capacity=4
// current slice=[2 3 6] length=3 capacity=4
// current slice=[2 3 6 7] length=4 capacity=4
比较不同的max
值下,capacity的值有何变化。
Slice扩容机制
Slice如何扩容的呢?
- 如果切片容量小于1024个元素,扩容的时候容量翻倍,即乘以2;如果切片容量超过1024个元素,扩容的时候增加原来容量的四分之一,即增长因子缩小为1.25。
- 如果扩容之后,还没有触及原数组的容量,那么切片的指针指向的位置,还是原数组;如果扩容之后,超过了原数组的容量,Go就会开辟一块新的内存,把原来的值拷贝过来,不会影响到原数组。
切片做为函数参数
切片也可以做为函数参数,语法类似于数组传递不指明数组大小。
实例:
package main
import (
"fmt"
)
func sumPartial(s []int) int {
res := 0
for i:=0; i< len(s); i++ {
res += s[i]
}
return res
}
func main() {
arr := [5]int {1, 2, 3, 4, 5}
slice := arr[1:3]
fmt.Printf("Sum of element 1 to 3 is: %d", sumPartial(slice))
}