截取也是创建slice的方法,可以从数组或则slice直接截取,需要指定起,止索引位置。
基于已有的slice创建新的slice对象,被称为 reslice。新的slice 和老的slice工用底层数组,新老slice对底层数组的更改都会影响到彼此。基于数组创建新的slice也是同样的效果,对数组或slice元素的更改都会影响到彼此。
值得注意的是,新老slice或则新的slice老数组相互影响的前提是共用底层数组,如果因为执行append操作使得新的slice或老的slice底层数组扩容,移动到新的位置,两者就不会相互影响了。所以关键在于两者是否共用底层数组。
data := []int{0,1,2,3,4,5,6,7,8,9}
slice := data[2:4:6] // data[low,high,max]
对data使用了3个索引值,截取出新的slice。这里data可以是数组或者是slice。low 是最低索引值,这里是闭区间,就是说第一个元素是data位于low索引处的元素;而high 和 max 则是开区间,表示最后一个元素只能是索引 high-1 处的元素,而最大容量则只能是索引 max-1 的元素。
要求: max >= high >= low
当high == low 时,新的slice为空。
还有一个点,high 和 max 必须在老数组或则老slice 的容量(cap)范围内。
我们可以来看一个栗子: 猜猜下面的代码,输出什么?
package main
import "fmt"
func main() {
slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := slice[2:5]
s2 := s1[2:6:7]
s2 = append(s2, 100)
s2 = append(s2, 200)
s1[2] = 20
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(slice)
}
运行结果如下:
结果分析:
s1 从slice索引 2 (闭区间)到索引 5(开区间,元素真正取到索引4),长度为3,容量默认到数组结尾8。
s2 从s1的索引2 (闭区间)到索引 6(开区间,元素真正取到索引5),容量到索引7(开区间,真正取到索引6),为5.
slice , s1 , s2的关系图如下:
注意,slice,s1和s2三者的元素指向同一个底层数组,向s2尾部追加一个元素100:
s2 = append(s2, 100)
此时,s2的容量刚好够,直接追加。不过,这会修改原始数组对应位置的元素,这一改动数组和s1都可以看到,如下图:
再次向s2追加元素200
s2 = append(s2, 200)
此时,s2的容量不够了,需要进行扩容。于是,s2,重新开辟一块空间,将原来的元素复制到新的位置,扩大自己的容量。并且为了应对未来可能append带来的再次扩容,s2会在此次扩容的时候多留一些buffer,将新的容量扩大到自己的2倍,也就是 10。slice , s1, s2的关系如下图:
注意,s2此时的底层数组已经和 slice,s1没有关系了。最后修改s1索引为2位置的元素:
s1[2] = 20
这次操作只会影响到原始数组的相应位置的元素,影响不到s2了,它已经不在这里了,如下图:
最后执行打印操作,打印s1时,只会打印s1长度以内的元素。所以只会打印出3个元素,虽然它底层数组不止3个元素。