Go语言中,函数传参都是值传递。如果我们希望传递一个数组,并且能够在函数中进行修改,那该如何做?使用指针当然可以,但作为21世纪的C语言,Go使用切片这种方式来实现这样的功能。
写一个切片
func main(){
arr := [...]int{0,1,2,3,4,5,6,7}
s := arr[2:6]
fmt.Println(s)
}
学过python的同学都知道,arr[2.6]表示的是从数组的第二个元素到第六个元素之前进行切片。输出为:[2 3 4 5]。
除此之外,arr[:6]表示从头到第六个元素之前元素的视图,arr[2:]表示从二开始切到最后一个,arr[:]则表示全部内容的视图。而Go中并不支持诸如[:-2]的视图,这在python中是具有意义的。
修改切片
如果对切片进行修改,数组会改变吗?
func main(){
arr := [...]int{0,1,2,3,4,5,6,7}
s := arr[2:6]
s[1] =10
fmt.Println(s,arr)
}
在这段代码中,我们对切片的第一个元素进行了修改,结果输出为[2 10 4 5] [0 1 2 10 4 5 6 7]。由此可见,对切片进行修改,不仅修改了切片的内容,也修改了原始的数组。
Slice本身是没有数据的,是对底层的视图。
对切片切片
func main(){
arr := [...]int{0,1,2,3,4,5,6,7}
s := arr[2:6]
s1 := s[:4]
s2 := s[2:]
fmt.Println(s,s1,s2)
}
执行这段代码,结果为[2 3 4 5] [2 3 4] [4 5]。可见我们可以对切片进行继续切片。
但如果新切片范围超过了原来的切片,会发生什么?
func main(){
arr := [...]int{0,1,2,3,4,5,6,7}
s := arr[2:6]
s1 := s[3:5]
fmt.Println(s,s1)
}
根据结果[2 3 4 5] [5 6], 可推至新的slice可以看得到数组右边没有被切的部分。
在slice的底层实现中,切片的长度可由len()得,而切片始到数组结尾可以用cap()获得。只要不大于cap()返回的值,切片就都可以对数组产生视图。也就是说Slice可以进行向后扩展(下标取值不能超越s[len(s)],视图不可以超越cap(s))。
向slice添加元素
如下列程序,我们对slice进行扩充:
func main(){
arr := [...]int{0,1,2,3,4,5,6,7}
s := arr[2]:6
s1 := s[3:5]
s2:= append(s1,10)
s3 := append(s2,11)
s4:= append(s3,12)
fmt.Println(s,s1,s2,s3,s4)
}
其结果为:[2 3 4 5] [5 6][5 6 10] [5 6 10 11] [5 6 10 11 12]。
可见,可以使用append对切片进行扩展。但,当添加元素超过cap(),系统会重新分配更大的底层数组。由于gc的存在,原数组如果不用可能会被释放掉,而扩展的切片也不再是原数组的视图。
创建一个Slice
可用以下代码创建一个切片:
var s []int
for i := 0; i<100; i++{
s = append(s,2*i+1)
}
fmt.Println(s)
也可以使用如下方法创建:
func main() {
s1 := []int{2, 4, 6, 8}
s2 := make([]int, 18)
fmt.Println(s1, s2)
}
可以直接初始化切片的值,也可以使用make来按指定长度创建slice。
切片的拷贝
可以使用copy()函数对slice进行拷贝
func main() {
s1 := []int{2, 4, 6, 8}
s2 := make([]int, 18)
copy(s2,s1)
fmt.Println(s1, s2)
}
结果为[2 4 6 8] [2 4 6 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
切片删除值
如何s2中删除第三个元素?
func main() {
s1 := []int{2, 4, 6, 8}
s2 := make([]int, 18)
copy(s2,s1)
s2 = append(s2[:3],s2[4:]...)
fmt.Println(s1, s2)
}
同样的,截掉头尾元素,可以使用诸如下面的方法
s2 = s2[1:]
s2 = s2[:len(s2-1)]