一、切片介绍
切片的类型规范为[]T,其中T是切片元素的类型。与数组不同,切片类型没有指定长度。
二、切片实现原理
Go语言slice底层实现是一个结构体,其中成员addr指向底层数组的头指针,length和capacity分别保存切片的长度和容量。
2.1、长度和容量的关系
- 对于nil切片,len和cap都为0
- 对于非nil切片,len不能超过cap
2.2、扩容规则
要增加切片的容量,必须创建一个新的、更大的切片将原始切片的内容复制到其中。
- 如果新的大小是当前大小2倍以上,则大小增长以新大小
- 如果当前大小小于1024,按每次2倍增长,否则循环一下操作:每次按当前大小1/4增长,知道增长的大小超过或者等于新大小
三、切片高效使用规范
func main() {
array := [...]string{"go", "c", "c++", "java"}
slice1 := array[0:2]
slice3 := append(slice1, "python")
slice3 = append(slice3, "ruby")
fmt.Println(array, slice3)
}
3.1、将一个切片附加到另一个切片
sliceA = append(sliceA, sliceB…)
3.2、可能的陷阱
如前所述,重新切片切片不会复制递增数组。完整的数据将保留在内存中,知道不再被引用为止。有时,这可能会导致程序在只需要一小部分数据时将所有数据保存在内存中。要解决此问题,可以在返回之前将感兴趣的数据复制到新切片
3.3、过滤
n := 0
for _, x := range arr {
if keep(x) {
a[n] = x
n++
}
}
a = a[:n]
3.4、插入
a = append(a[:i], append([]T{x}, a[i:]...)...)
3.5、扩容
确保有足够的空间追加n个元素,而无需重新分配
if cap(arr) - len(arr) < n {
a = append(make([]T, 0, len(a) + n), a...)
}
3.6、追加N个元素
a = append(a, make([]T, n)...)
四、总结
- slice是边长数组,通过切片表达式创建时,底层数组是共享的,相当于两个指针指向同一地址,此时对slice元素的修改,会影响到共享数组或slice
- 对slice进行append等操作时,可能会造成slice的自动扩容,扩容时会重新申请一个底层数组,该slice底层指针会指向新的地址,如果该slice之前和其他数组共享底层数组,此时两个指针就指向不同的地址,不在共享底层数组了
五、推荐学习资料
切片结构介绍 https://go.dev/slices-intro
切片append原理 https://go.dev/slices
切片使用技巧 https://github.com/golang/go/wiki/SliceTricks
https://research.swtch.com/godata