Go 切片
切片与数组相比,不需要设定长度,在[]中不用设定值,相对来说比较自由。
slice本身没有数据,是对底层array的一个view对slice所做的任何修改都将反映在底层数组中。
切片是什么?一种数据结构,类似数组,围绕动态数组的概念而设计,可按需自动改变大小。
切片内部实现?切片基于数组实现,底层是数组(故底层的内存是连续分配,可根据索引获取数据,可迭代以及垃圾回收),自身非常小(切片对象只有3个字段数据结构即:指向底层数组的指针ptr、切片长度len、切片容量cap),可看作对底层数组的抽象。
切片如何声明?创建方式多样,比如make、或指定初始化值或基于现有切片(数组)创建
方法一简洁如make make([]T, length, capacity) ,比如 slice := make([]int,5) 指定切片长度为5, slice:=make([]int,5,10) 指定切片长度5,容量10(对应的是切片底层数组)
注:容量为10,但长度为5,故我们只能访问5个元素,剩下的5个元素需要切片扩充后才可访问。且容量>=长度。
方法二使用自变量:指定初始化值 slice := []int{1,2,3,4,5}
方法三基于现有切片(数组)创建
基于两个索引 s := arr[startIndex:endIndex] //前闭后开 使用 [i:j] 操作符表示以i到j的左闭右开,类似Java的subString方法。
注:新切片和原切片共用一个底层数组,故当修改时候,底层数组的值会改变。
slice := []int{1,2,3,4,5} newSlice := slice[1:3] newSlice[0]=10
fmt.Println(slice) //输出是:[1 10 3 4 5] fmt.Println(newSlice) // [10 3]
基于三个索引 slice[i,j,k] //k为限定新切片容量<原切片最大索引值
slice := []int{1,2,3,4,5} newSlice := slice[1:2:3] fmt.Printf("newSlice长度:%d,容量:%d",len(newSlice),len(newSlice)) //输出:长度为1 容量为1
切片初始值?切片底层为数组,创建切片不指定字面值,默认为数组元素的零值。类似数组,初始化可以为:
//数组初始化 array := [5]int{4:1} slice := []int{4:1}
nil切片与空切片区别?长度、容量都为0.
nil切片(比如: var nilSlice []int )指向底层数组的指针为nil,nil切片表示不存在的切片。
空切片(比如: slice := []int{} )对应的指针是个地址,空切片表示一个空集合。
基于原数组或切片创建一个新切片 新切片容量和大小是多少?
对底层数组容量是k的切片slice[i:j]而言,长度为: j-i ,容量为: k-i ,Go提供内置的 len 和 cap 来计算切片长度和容量。
slice := []int{1,2,3,4,5} newSlice := slice[1:3] fmt.Printf("newSlice长度:%d,容量:%d",len(newSlice),len(newSlice)) // 输出:长度2 容量2 3-1=2 5-3=2
如何使用切片?类似使用数组,通过索引获取切片对应元素的值,也可修改对应元素的值。
切片只能访问到长度内的元素,长度外的如果访问会运行时异常。
slice := []int{1,2,3,4,5} fmt.Println(slice[2]) //3 slice[2] = 10 fmt.Println(slice[2]) //10
切片如何按需增长?使用内置函数 append 可为一个切片追加一个元素
添加元素:s = append(s,val) 如果超越cap,系统会重新分配更大的底层数组。
append函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其它slice。
但当slice中没有剩 余空间(即(cap-len) == 0)时,此时将动态分配新的数组空间。
返回的slice数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的slice则不受影响
slice := []int{1,2,3,4,5} newSlice := slice[1:3] newSlice = append(newSlice,10) fmt.Println(newSlice) //[2 3 10] fmt.Println(slice) //[1 2 3 10 5]
append特点?智能增长底层数组的容量,当容量小于1000,成倍增长,容量超过1000,增长因子设为1.25,每次增加25%。
可变参:可同时追加好几个值
slice := []int{1,2,3,4,5} newSlice := slice[1:2:3] newSlice1 := slice[1:2] fmt.Println(newSlice1) //[2] newSlice = append(newSlice,slice ...) fmt.Println(newSlice) //[2 1 2 3 4 5] fmt.Println(slice) //[1 2 3 4 5]
如何迭代切片? for range
slice := []int{1,2,3,4,5} for _,v := range slice{ fmt.Printf("值%d \n",v) } for i:=0;i<len(slice);i++{ fmt.Printf("value:%d\n",slice[i]) }
函数间传递切片?
func main() { slice := []int{1,2,3,4,5} fmt.Printf("%p \n",&slice) modify(slice) fmt.Println(slice) } func modify(slice []int){ fmt.Printf("%p\n",&slice) slice[2] = 10 }
0xc420084020
0xc420084040
[1 2 10 4 5]
例子:
func main() { var numbers []int printSlice(numbers) /* 允许追加空切片 */ numbers = append(numbers, 0) printSlice(numbers) /* 向切片添加一个元素 */ numbers = append(numbers, 1) printSlice(numbers) /* 同时添加多个元素 */ numbers = append(numbers, 2,3,4) printSlice(numbers) /* 创建切片 numbers1 是之前切片的两倍容量*/ numbers1 := make([]int, len(numbers), (cap(numbers))*2) /* 拷贝 numbers 的内容到 numbers1 */ copy(numbers1,numbers) printSlice(numbers1) } func printSlice(x []int){ fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x) }
输出:
len=0 cap=0 slice=[] len=1 cap=1 slice=[0] len=2 cap=2 slice=[0 1] len=5 cap=6 slice=[0 1 2 3 4] len=5 cap=12 slice=[0 1 2 3 4]