切片(slice)
Go 语言切片是对数组的抽象。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大
切片是一个引用类型,他的结构包含 地址
,长度
,容量
package main
import "fmt"
func main() {
var s1 []int //申明一个存放int类型元素的切片
var s2 []string//申明一个string类型元素的切片
fmt.Println(s1,s2) //空表示没有开辟内存空间
fmt.Println(s1==nil)//true
fmt.Println(s2==nil)//true
//初始化
s3:=[]int{1,2,3,4}
s4:=[]string{"四川","重庆","湖南"}
fmt.Println(s3,s4)
fmt.Println(s3==nil)//false
fmt.Println(s4==nil)//false
//长度和容量
fmt.Printf("len(s3):%d cap(s3):%d\n",len(s3),cap(s3))
fmt.Printf("len(s4):%d cap(s4):%d\n",len(s4),cap(s4))
//由数组得到切片 注意:后面的数字表示长度
s5:=[...]int{1,2,3,4,5}
s5_1:=s5[0:3] //[1 2 3]
s5_2:=s5[:3] //[0:3] [1 2 3 ]
s5_3:=s5[1:] //[0:3] [2 3 4 5]
s5_4:=s5[:]//[0:len(s5)] [1 2 3 4 5]
fmt.Println(s5_1,s5_2,s5_3,s5_4)
//注:切片的容量是指底层数组的容量
fmt.Printf("len(s5_1):%d cap(s5_1):%d\n",len(s5_1),cap(s5_1))//len(s5_1):3 cap(s5_1):5
//底层数组从切片的第一个元素到最后元素的数量
fmt.Printf("len(s5_3):%d cap(s5_3):%d\n",len(s5_3),cap(s5_3))//len(s5_3):4 cap(s5_3):4
//切片再切片
s6:=s5_3[3:]
fmt.Printf("len(s6):%d cap(s6):%d\n",len(s6),cap(s6))//len(s6):1 cap(s6):1
//修改了底层数组;注:切片是引用类型
s5[4]=500
fmt.Println(s5_3)//[2 3 4 500]
fmt.Println(s6)//[500]
//make函数创建切片
s7:=make([]int,5,10)
fmt.Printf("s7-%v len(s7):%d cap(s7):%d\n",s7,len(s7),cap(s7)) //s7-[0 0 0 0 0] len(s7):5 cap(s7):10
}
总结:
- 切片是指向了一个底层数组
- 切片的长度就是他元素的个数
- 切片的容量是底层数组的第一个元素到最后一个元素的数量
切片的本质
切片就是一个框,框柱了一块内存,属于引用类型,真正的数据都是存在底层数组里的
- 切片之间是不能进行比较的,不能使用
==
来判断两个切片是否全部等元素,切片唯一合法的比较是和nil
比较;一个nil
值的切片并没有底层数组,一个nil
值的切片长度和容量都是0;但我们不能说一个长度容量都是0的切片一定是nil
package main
import "fmt"
func main() {
var s1 []int //一个切片在未初始化之前默认为 nil,长度为 0
s2:=[]int{}
s3:=make([]string,0,0)
fmt.Printf("s1:%T值为nil 表示没有开辟内存空间 len(s1)=%d cap(s1)=%d (s1==nil)=%v \n",s1,len(s1),cap(s1),s1==nil)
fmt.Printf("s2:%T值为0 表示开辟了内存空间 len(s2)=%d cap(s2)=%d (s2==nil)=%v \n",s2,len(s2),cap(s2),s2==nil)
fmt.Printf("s3:%T值为0 表示开辟了内存空间 len(s3)=%d cap(s3)=%d (s3==nil)=%v \n",s3,len(s3),cap(s3),s3==nil)
}
append 和copy
package main
import "fmt"
func main() {
var numbers []int //申明一个存放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)
}
append 练习
package main
import "fmt"
func main() {
s1:=[]string{"北京","上海","深圳"}
fmt.Printf("s1 len=%d cap=%d slice=%v\n",len(s1),cap(s1),s1)
//s1[3]="四川"//错误的语法会编译出错,索引越界
//调用append函数必须用原来的切片变量接收返回值
s1=append(s1,"四川")
fmt.Printf("s1 len=%d cap=%d slice=%v\n",len(s1),cap(s1),s1)
s1=append(s1,"杭州","武汉")
fmt.Printf("s1 len=%d cap=%d slice=%v\n",len(s1),cap(s1),s1)
ss:=[]string{"武汉","西安","南京"}
s1=append(s1,ss...)//...表示拆开
fmt.Printf("s1 len=%d cap=%d slice=%v\n",len(s1),cap(s1),s1)
}
结果
s1 len=3 cap=3 slice=[北京 上海 深圳]
s1 len=4 cap=6 slice=[北京 上海 深圳 四川]
s1 len=6 cap=6 slice=[北京 上海 深圳 四川 杭州 武汉]
s1 len=9 cap=12 slice=[北京 上海 深圳 四川 杭州 武汉 武汉 西安 南京]
copy练习
package main
import "fmt"
func main() {
a1:=[]int{1,2,3,4}
a2:=a1//赋值
//var a3 []int//nil
var a3=make([]int,3,3)
copy(a3,a1)
fmt.Println(a1,a2,a3)
a1[0]=100;
fmt.Println(a1,a2,a3)
//将a1索引为1的3这个元素给删除掉
a1=append(a1[:1],a1[2:]...)
fmt.Println(a1)
}
结果:
[1 2 3 4] [1 2 3 4] [1 2 3]
[100 2 3 4] [100 2 3 4] [1 2 3]
[100 3 4]
注意:copy到目的切片需要定义好长度和容量