切片slice是一个可变长度的序列,切片中的每个元素都是相同类型,我们可以将切片看做动态数组
切片是引用类型,是对数组的引用切片的大小是动态分配的切片的遍历和访问切片元素操作和数组类似 package main import "fmt" func main(){ var demo = []int{1,2,3,4,5,6,7} fmt.Println(len(demo),cap(demo)) child := demo[2:4] fmt.Println(child,len(child),cap(child)) }go run main.go
7 7 [3 4] 2 5为什么是这样的呢?我们模拟一下在内存中的布局
上图中 ptr表示地址指针, len表示长度, cap表示容量
切片的创建通常有两种方式
基于某个数组创建切片直接创建切片使用内置函数make()创建切片var name []T
name 表示切片的变量名T 表示切片类型对应的元素类型切片只是声明,并且没有初始化那么切片还未分配内存,默认值是nil
package main import "fmt" func main(){ //声明一个整型切片 var nSlice []int //声明一个字符串切片 var sSlice []string //声明一个字符串类型的空切片 var sEmptySlice = []string{} fmt.Println(nSlice,sSlice,sEmptySlice) // 切片长度都是0 fmt.Println(len(nSlice),len(sSlice),len(sEmptySlice)) // 未初始化的切片默认值是nil fmt.Println(nSlice == nil) fmt.Println(sSlice == nil) // 空切片也是分配的内存,默认值不是nil fmt.Println(sEmptySlice == nil) } $ go run main.go [] [] [] 0 0 0 true true false⭐️ 切片slice 操作符s[i:j] (0<=i<=j<=cap(s)) 创建一个新的slice ,新的slice引用了序列s中从i到j-1索引位置的所有元素,这个s可以是数组可以是指向数组的指针,可以是slice,新的slice的元素个数是j-i个,如果省略了i,那么新的slice其实索引位置是0,如果省略了j ,那么新slice结束索引位置是len(s)-1,就是 j = len(s)
package main import "fmt" func main() { // 初始化一个数组 var nArr = [10]int{0,1,2,3,4,5,6,7,8,9} s := nArr[2:8] // s 是一个int类型的切片 fmt.Printf("s 的类型是 = %T\n",s) fmt.Printf("s 的元素是 = %v\n",s) fmt.Printf("s 的元素个数是 = %d\n",len(s)) fmt.Printf("s 的容量是 = %d\n",cap(s)) } $ go run main.go s 的类型是 = []int s 的元素是 = [2 3 4 5 6 7] s 的元素个数是 = 6 s 的容量是 = 8格式
make([]T,size,cap)
T : 切片类型size : 切片分配的元素个数cap : 切片的容量说明 :
内置函数make()创建的切片,可以自己指定切片大小和容量 ,若是指定的切片容量就必须不小于切片的大小,否则编译报错make()创建的切片没有给元素赋值,那么就是自动采用类型默认值make()创建的切片对应的数组由make底层维护,外部不可见 package main import "fmt" func main(){ var mSlice1 []string = make([]string,3,5) fmt.Printf("mSlice1的长度是%d,容量数%d,内容是%v\n", len(mSlice1), cap(mSlice1), mSlice1) var mSlice2 []string = make([]string,3,3) mSlice2[0] = "tom" mSlice2[1] = "kobe" mSlice2[2] = "jack" fmt.Printf("mSlice2的长度是%d,容量数%d,内容是%v\n", len(mSlice2), cap(mSlice2), mSlice2) } $ go run main.go mSlice1的长度是3,容量数5,内容是[ ] mSlice2的长度是3,容量数3,内容是[tom kobe jack]Go语言的内置函数append() 可以为切片动态添加元素,切片指向一片内存空间,可以空间能容纳一定数量的元素,当空间不能容纳足够多的元素时,切片就会进行扩容,这个操作一般发生在append()函数调用的时候
当切片扩容扩容的时候切片的容量按2的倍数扩充
len()函数是参看切片拥有的元素个数cap()函数是查看切片容量的情况,"扩容"是容量的增扩 package main import "fmt" func printSlice(n []int){ // 观察slice 的长度和容量变化 fmt.Printf("切片n的长度是%d,容量是%d,内存地址是 %p\n",len(n),cap(n),&n) } func main(){ // 定义一个切片 var n []int // 打印切片信息 printSlice(n) // 给切片添加18个元素 for i:=0;i<18;i++{ n = append(n,i) printSlice(n) } } 切片n的长度是0,容量是0,内存地址是 0xc000004480 切片n的长度是1,容量是1,内存地址是 0xc0000044c0 切片n的长度是2,容量是2,内存地址是 0xc0000044e0 切片n的长度是3,容量是4,内存地址是 0xc000004500 切片n的长度是4,容量是4,内存地址是 0xc000004520 切片n的长度是5,容量是8,内存地址是 0xc000004540 切片n的长度是6,容量是8,内存地址是 0xc000004560 切片n的长度是7,容量是8,内存地址是 0xc000004580 切片n的长度是8,容量是8,内存地址是 0xc0000045a0 切片n的长度是9,容量是16,内存地址是 0xc0000045c0 切片n的长度是10,容量是16,内存地址是 0xc0000045e0 切片n的长度是11,容量是16,内存地址是 0xc000004600 切片n的长度是12,容量是16,内存地址是 0xc000004620 切片n的长度是13,容量是16,内存地址是 0xc000004640 切片n的长度是14,容量是16,内存地址是 0xc000004660 切片n的长度是15,容量是16,内存地址是 0xc000004680 切片n的长度是16,容量是16,内存地址是 0xc0000046a0 切片n的长度是17,容量是32,内存地址是 0xc0000046c0 切片n的长度是18,容量是32,内存地址是 0xc0000046e0append()方法可以一次性添加多个元素到切片中
package main import "fmt" func main(){ var s []string = []string{"a"} fmt.Printf("切片n的长度是%d,容量是%d,内容是 %v\n",len(s),cap(s),s) s =append(s,"b","c","d","e") fmt.Printf("切片n的长度是%d,容量是%d,内容是 %v\n",len(s),cap(s),s) } $ go run main.go 切片n的长度是1,容量是1,内容是 [a] 切片n的长度是5,容量是5,内容是 [a b c d e]append()可以将同类的切片追加到另一个切片后面
package main import "fmt" func main(){ var s []string = []string{"a","b","c"} fmt.Printf("切片n的长度是%d,容量是%d,内容是 %v\n", len(s), cap(s), s) var s1 []string = []string{"w","x","y","z"} //注意这个写法,要是直接写s1报错 `cannot use s1 (type []string) as type string in append` s = append(s,s1...) fmt.Printf("切片n的长度是%d,容量是%d,内容是 %v\n", len(s), cap(s), s) } $ go run main.go 切片s的长度是3,容量是3,内容是 [a b c] 切片s的长度是7,容量是7,内容是 [a b c w x y z]切片的遍历与数组的遍历一样,通常为两种方式
for 循环遍历for -range 结构遍历 package main import "fmt" func main(){ var arr = [...]int{100,2000,300,40,58,6,7,8,9,0,11,12,13} s := arr[:9] L := len(s) // 使用for循环遍历slice for i:=0 ;i<L ;i++{ fmt.Printf("%d\t",s[i]) } fmt.Println(""); // 使用for-range 结构遍历切片 for _,v := range s{ fmt.Printf("%d\t",v) } } $ go run main.go 100 2000 300 40 58 6 7 8 9 100 2000 300 40 58 6 7 8 9Go语言中没有专门的语法或者函数方法去删除切片中的元素,实现删除切片中的元素需要借助切片本身的 特点来实现
在Go语言中删除切片中元素,实际上就是以被删除元素为分界点,将前后两部分再重新连接起来
package main import "fmt" func main() { // 定义一个切片 var s = []int{100, 2000, 300, 40, 58, 6, 7, 8, 9, 0, 11, 12, 13} fmt.Printf("len = %d,cap = %d\n%v\n",len(s),cap(s),s) // 删除指定的元素 delIndex := 2 fmt.Println(s[:delIndex]," ",s[delIndex+1:]) s = append(s[:delIndex],s[delIndex+1:]...) fmt.Printf("len = %d,cap = %d\n%v\n",len(s),cap(s),s) } $ go run main.go len = 13,cap = 13 [100 2000 300 40 58 6 7 8 9 0 11 12 13] [100 2000] [40 58 6 7 8 9 0 11 12 13] len = 12,cap = 13 [100 2000 40 58 6 7 8 9 0 11 12 13]在Go语言中内建函数copy() ,可以迅速的将一个切片的数据复制到另外一个切片空间中
copy()函数的格式
func copy(dst, src []Type) int
dst 是目标 切片src 是源切片,就是将src的元素复制到dst切片中执行该函数后返回的是实际发生复制的元素个数 package main import "fmt" func main(){ var s1 []int = []int{1,2,3,4,5} var s2 []int = []int{7,8,9} var s3 []string = []string{"a","b","c","d","e"} var s4 []string = []string{"x","y"} var s5 []string = []string{"C","java","javascript","golang"} var s6 []string = []string{"C语言","java语言","javascript语言","Go语言"} n1 := copy(s2,s1) n2 := copy(s3,s4) n3 := copy(s5,s6) fmt.Printf("复制的元素个数%d,s2 = %v\n",n1,s2) fmt.Printf("复制的元素个数%d,s3 = %v\n",n2,s3) fmt.Printf("复制的元素个数%d,s5 = %v\n",n3,s5) } $ go run main.go 复制的元素个数3,s2 = [1 2 3] 复制的元素个数2,s3 = [x y c d e] 复制的元素个数4,s5 = [C语言 java语言 javascript语言 Go语言]切片复制总结 :
使用copy()实现切片复制的目标切片和源切片必须是具有相同的元素类型长度相等的两个切片复制,源切片的元素会全部复制给目标切片,此时目标切片的内容和源切片完全一致元素长度不等的两个切片进行复制,复制的元素个数以小的切片为准如果目标切片长度大于原切片长度,那么源切片的所有元素,都复制给目标切片索引相同的位置如果目标切片长度小于源切片长度,那么目标切片相同索引位置的元素内容,被源切片相同索引位置的内容覆盖