原创

go语言的切片研究

温馨提示:
本文最后更新于 2022年03月31日,已超过 727 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

切片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)
   }
}
正文到此结束
本文目录