一、切片:
Go 语言切片Slice是对数组的抽象,是引用类型。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组")。
与数组相比,切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
[5]int
是数组,而 []int
是切片。
切片的数据结构中,包含一个指向数组的指针
array
,当前长度
len
,以及最大容量
cap
。
在使用
make([]int, len)
创建切片时,实际上还有第三个可选参数
cap
,也即
make([]int, len, cap)
。
在不声明
cap
的情况下,默认
cap=len
。
当切片长度没有超过容量时,对切片新增数据,不会改变
array
指针的值。
二、切片扩容
- 当原切片长度小于1024时,新切片的容量会直接翻倍。
- 而当原切片的容量大于等于1024时,会反复地增加25%,直到新容量超过所需要的容量。
- 当需要的容量超过原切片容量的两倍时,会使用需要的容量作为新容量。
三、定义切片
1.声明var定义一个nil的切片。
var tmp []int
2.使用make定义一个指定长度的切片
var tmp []int = make([]int ,10)
3.使用推导式
tmp := make([]int, 10)
4.也可以指定容量,其中 capacity 为可选参数。
tmp make([]T, length, capacity)
注意:nil切片和空切片的区别
nil切片:
// 创建 nil 整型切片
var myNum []int
空切片:
// 使用 make 创建空的整型切片 myNum := make([]int, 0) // 使用切片字面量创建空的整型切片 myNum := []int{}
无论是nil切片,还是空切片,都可以使用append, copy等方法。
无论是nil切片,还是空切片,都可以使用len(),他们的长度都是0。
要注意的是nil切片 == nil,但是 空切片 != nil。
四、切片初始化
s :=[] int {1,2,3 }
s := arr[:]
五、切片操作
1.append
s1 := []int{1, 2, 3, 4, 5} //短操作符声明 len为4,cap为4 s2 := make([]int, 2, 4) //make语法声明 ,len为2,cap为4 s3 := append(s2, 7) //append一个元素 s4 := append(s2, s1...) //append 一个切片所有的元素
2.copy
copy(s1, s2) // 复制,用s2的元素填充s1里去,改变原slice,覆盖对应的key
3.删除一个元素
//删除第三个元素
s7 := append(s1[:1], s1[3:]...)
4.修改
// 修改原始数据的第一个元素
s7[0] = 999
5.遍历
// 切片遍历 var arr [5]int = [...]int{10,20,30,40,50} slice := arr[1:4] // for循环方式 for i := 0; i < len(slice); i++ { fmt.Printf("slice[%v]=%v", i, slice[i]) } fmt.Println() // for range方式 for index, value := range slice { fmt.Printf("i=%v v=%v\n", index, value) }
6.
sli := slice[:] // 引用一个切片
slice = slice[:0] // 清空一个切片
六、切片的几个坑
由于切片是引用类型,不是值类型。类似于py的列表,也是引用类型。
第一个坑:子切片和父切片用的是同一个底层数组
pSlice := []int{0, 10, 20, 30, 40} //新建一个子切片,指定元素为pSlice的index 1到3(不包括3),即{10, 20} subSlice := pSlice[1:3] //这时候,改变subSlice的值,也会改动到pSlice subSlice[1] = 22 fmt.Println(pSlice) //{0, 10, 22, 30, 40} fmt.Println(subSlice) //{10, 22}
append,同样会产生这样的问题。
//append操作会返回一个新切片,同样共享底层数组 subSlice = append(subSlice, 33) 这时候两个slice的值如下: pSlice //{0, 10, 22, 33, 40} subSlice //{10, 22, 33}
第二个坑:for range遍历的是副本