Slice学习笔记
一、之前有学过golang的数组,然后在golang中经常使用的不是数组而是Slice,Slice是什么呢?
1、中文名叫切片,顾名思义,就像古画中的一些赝品,说它是赝品呢,却也不是,因为它是从真迹中剥落下来的一层。就如同这个一样,Slice其实就是对数组的一个切层,这一层薄如纸片,所以就叫切片(ps 方便理解而抽象出来的解释)
1.1、先来看看是如何切出来的
func main() {
arr := [...]int{0,1,2,3,4,5,6,7}
s := arr[2:6] // s就是arr的一个切片
那这个s里面有些什么元素呢,同python中一样,左开右闭地从arr中取下标对应的元素,所以s中就只有[2,3,4,5]这四个元素。
1.2、那既然是切下来的,有什么不同,有什么相同,又有什么联系呢?
不同点
首先,创建方式不同
func main() {
// 创建数组
arr := [...]int{0,1,2,3,4,5,6,7} // 不定长数组
arr1 := [3]string{"哈哈","嘿嘿","嘻嘻"} // 定长数组
// arr2 := [3]int{1,2,3,4} 编译不通过,因为指定了长度,那里面的元素就只能小于等于指定长度
// 切片创建
s := []int{1,2,3} // 前面我说它是从一个数组上切下来的,那它是从哪个数组切下来的呢?可以认为它是从arr := [...]int{1,2,3}上切下来的,但是这个数组不是真实存在的,是由系统隐式创建的。
}
其次,数组的类型是由两个指标决定的,一是它的长度(不同长度数组,类型不同),二是它里面元素类型
再有,数组是没有append方法的,想要扩容数组,必须先获取它的切片,再使用append方法,只有切片才有append方法,slice的扩容机制是2倍扩容,数组是值传递的,要么使用它的指针,可以做到引用传递,而切片是引用传递(操作之后会影响原数组)
相同点
很直观的可以看到,他们的相同点就是可以容纳一组数据,是一个数据的集合,底层最终都是在内存中一段连续的空间(数组)
联系
他们之间的关系的话,前面有说到过,Slice是对一个数组的剥落下来的一层,那这层可能不完整,术语叫做视图(view),就是说Slice是数组的一个视图,它里面的元素改变会影响着所对应的数组中的元素随着改变,比如:
func updateSlice(s []int) {
s[0] = 100
}
func main() {
arr := [...]int{0,1,2,3,4,5,6,7}
s1 := arr[2:]
fmt.Println("s1:", s1)
updateSlice(s1)
fmt.Println("After updateSlice s1")
fmt.Println(s1)
fmt.Println(arr)
}
// 结果是
s1: [2 3 4 5 6 7]
After updateSlice s1
[100 3 4 5 6 7]
[0 1 100 3 4 5 6 7]
可以看到,这里把s的第一个元素修改为了100,而s对应的数组中的那个元素(s的第一位就是数组的第三个元素)也被修改了。
切片可以再取切片,叫做ReSlice操作,而在这个操作中又一个比较有意思的情况会出现
func main() {
arr := [...]int{0,1,2,3,4,5,6,7}
s1 = arr[2:6] // [2,3,4,5]
s2 = s1[3:5]
fmt.Println(s2)
}
// 结果
[5,6]
s1取的是数组中的2-6的下标元素,即[2,3,4,5],就这四个元素,然后s2要从s1中取出下标为3-5的元素,包含两个元素,而在s1中下标为3的就已经是5了,按道理来说就只能取出5,这一个元素来,但是结果却取出了两个,竟然还包含了s1中没有的6,我们来看一个图吧
可以看到s1虽然只取到了arr中4个元素,但是它可以扩展的,它可以把原来arr中后面没取到的元素全部扩展出来(扩展的元素,通过下标来取是取不到的),所以当对s1再次切片的时候,是可以切到扩展元素的。
那接着看看这个Slice的底层是怎样的呢,仍然用一张图来看吧
从图中可以看到,slice中是有一个指针的,这个指针呢指向切片中的第一个元素,len呢就是当前这个切片的实际长度,cap呢是指这个切片的最大容量长度,而这个最大容量长度呢,就是对应的从数组中切片时起始位置到数组的最后一个元素的长度。
也就是说一个切片是可以扩展的,但是只能向后扩展,不能向前,向后扩展的最大长度呢就是这个cap的值了,超出这个值就会报错。
这里简单的讲一两个slice的操作吧。
前面反复的提到过的一个append操作,就是往slice中添加元素,append操作呢必须要用一个slice去接收,如果append操作的时候超过了cap的大小,这个时候接收到的slice,它就不再是原数组的视图,系统将会生成一个隐式的数组,而这个slice就是这个隐式数组的视图(即一个全新的slice),而且这个slice的cap会是原来的2倍。
slice的复制操作
copy(目标slice,原slice)
// 将s1复制到s2中
copy(s2, s1)
结束语
以上就是对slice的一些个人学习理解,后续有新的理解,继续补充。