切片slice
Go 语言切片是对数组的抽象。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
声明切片
package main import "fmt" func main() { //1:direct declare variable var s1 \[\]int //s1 = \[\]int{} fmt.Printf("s1: %v,len: %v,cap: %v \\n",s1,len(s1),cap(s1)) //2:assignment variable s2 := \[\]int{1,2,3} fmt.Printf("s2: %v,len: %v,cap: %v \\n",s2,len(s2),cap(s2)) //3:make slice s3 :=make(\[\]int,0) fmt.Printf("s3 %v,len: %v,cap: %v \\n",s3,len(s3),cap(s3)) //3:make slice and assignment slice length s4 :=make(\[\]int,0,10) fmt.Printf("s4: %v,len: %v,cap: %v \\n",s4,len(s4),cap(s4)) //4:slice by array arr := \[5\]int{1,2,3,4,5} s5 := arr\[:\] fmt.Printf("s5: %v,len: %v,cap: %v \\n",s5,len(s5),cap(s5)) s6 := arr\[1:len(arr)-1\] fmt.Printf("s6: %v,len: %v,cap: %v \\n",s6,len(s6),cap(s6)) //5:slice by slice s7 := \[\]int{1,2,3,4,5,6,7,8,9,10} fmt.Printf("s7: %v,len: %v,cap: %v \\n",s7,len(s7),cap(s7)) s8 := s7\[1:len(arr)-1\] fmt.Printf("s8: %v,len: %v,cap: %v \\n",s8,len(s8),cap(s8)) //\[x:y:z\] x:start index y:end index z:slice cap=z-x s9 := s7\[6:10:10\] fmt.Printf("s9: %v,len: %v,cap: %v \\n",s9,len(s9),cap(s9)) }
切片cap,len
cap
切片容量,在追加切片时,可能会使得切片容量变大
len
切片长度,表示切片数组的长度
package main import "fmt" func main() { var s1 \[\]int fmt.Printf("s1: %v,len: %v,cap: %v \\n",s1,len(s1),cap(s1)) s1 = \[\]int{1,2,3} fmt.Printf("s1: %v,len: %v,cap: %v \\n",s1,len(s1),cap(s1)) var arr =\[\]int{1,2,3,4,5,6,7,8,9,10} s2 := arr\[0:5\] fmt.Printf("s2: %v,len: %v,cap: %v \\n",s2,len(s2),cap(s2)) }
输出:
可以看到,在数组中获取切片后,cap=10,len=5
切片的cap永远是大于等于len的
空nil切片
切片未初始化之前,切片等于nil,len和cap都为0
操作切片
append 切片追加数据
package main import ( "fmt" ) func main() { var s1 = make(\[\]int,0,10) fmt.Printf("s1: %v,len: %v,cap: %v \\n",s1,len(s1),cap(s1)) s1 = append(s1,1,2,3,4 ) fmt.Printf("s1: %v,len: %v,cap: %v \\n",s1,len(s1),cap(s1)) }
当追加的数据超出cap容量时,将会触发切片重新分配底层数组,即时原数组并未被填满
重新分配底层数组时,一般以原有cap进行二倍扩容分配
package main import ( "fmt" ) func main() { var s1 = make(\[\]int,0,4) fmt.Printf("s1: %v,len: %v,cap: %v \\n",s1,len(s1),cap(s1)) s1 = append(s1,1,2,3,4) fmt.Printf("1 s1 pointer: %p \\n",s1) fmt.Printf("s1: %v,len: %v,cap: %v \\n",s1,len(s1),cap(s1)) fmt.Printf("2 s1 pointer: %p \\n",s1) s1 = append(s1,5,6) fmt.Printf("s1: %v,len: %v,cap: %v \\n",s1,len(s1),cap(s1)) fmt.Printf("3 s1 pointer: %p \\n",s1) }
注意:由于每次超出时都会重新分配数组,在开发时应该避免重新分配,一次性定义好切片的容量
copy
copy函数可以将一个切片的元素复制到另一个切片,复制的长度由最小的切片长度为准:
package main import ( "fmt" ) func main() { var s1 = make(\[\]int, 0, 4) var s2 = make(\[\]int, 0, 10) s1 = append(s1, 9) s2 = append(s2, 1, 2, 3, 4, 5, 6) copy(s2,s1) fmt.Printf("s1: %v,len: %v,cap: %v \\n", s1, len(s1), cap(s1)) fmt.Printf("s2: %v,len: %v,cap: %v \\n", s2, len(s2), cap(s2)) }
输出:
s1: \[9\],len: 1,cap: 4 s2: \[9 2 3 4 5 6\],len: 6,cap: 10
当s1复制到s2时,s1的元素将完全复制到s2中(如果s2容量比s1长度小,则会忽略s1超出的部分)
切片copy自身
当切片 通过[:x]方式创建新切片时,将直接使用原有切片的地址,同时如果新切片发生了更改,原有切片也将发生更改:
package main import ( "fmt" ) func main() { s1 :=\[\]int{1,2,3,4,5,6,7,8,9} s2 :=s1\[8:\] s3 :=s1\[:8\] fmt.Printf("s1 pointer:%p \\n", s1) fmt.Printf("s2 pointer:%p \\n", s2) fmt.Printf("s3 pointer:%p \\n", s3) //s2 pointer == s3 pointer copy(s3,s2) fmt.Printf("s3: %v,len: %v,cap: %v \\n", s3, len(s3), cap(s3)) fmt.Printf("s1: %v,len: %v,cap: %v \\n", s1, len(s1), cap(s1)) }
输出:
s1 pointer:0xc00001e0a0 s2 pointer:0xc00001e0e0 s3 pointer:0xc00001e0a0 s3: \[9 2 3 4 5 6 7 8\],len: 8,cap: 9 s1: \[9 2 3 4 5 6 7 8 9\],len: 9,cap: 9
切片遍历
可直接通过range遍历:
package main import ( "fmt" ) func main() { s1 := \[\]int{1, 2, 3, 4, 5, 6, 7, 8, 9} fmt.Printf("s1: %v,len: %v,cap: %v \\n", s1, len(s1), cap(s1)) for key, value := range s1 { fmt.Printf("key:%v,value:%v \\n",key,value) } }