切片
切片(slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。
切片是一个引用类型,它的内部结构包括地址、长度和容量。
声明切片类型的基本语法如下:
var name []T
其中name表示变量名,T表示切片中的元素类型
var arr1 []int
fmt.Printf("%v-%T-长度:%v", arr1, arr1, len(arr1))//[]-[]int-长度:4
方式二:定义时初始化
var arr1 = []int{1, 3, 5, 7}
fmt.Printf("%v-%T-长度:%v", arr1, arr1, len(arr1))//[]-[]int-长度:4
方式三:赋值时带下标
var arr1 = []int{1:2, 2:4, 5:6}
fmt.Printf("%v-%T-长度:%v", arr1, arr1, len(arr1))
//[0 2 4 0 0 6]-[]int-长度:6
nil
golang中,当声明了一个变量,却还并没有赋值值,golang中会自动赋值一个默认值。
类型 | 默认值 |
---|---|
bool | false |
numbers | 0 |
string | "" |
pointers | nil |
slices(切片) | nil |
maps/channels/functions | nil |
golang中声明切片的默认值是nil
var arr1 []int
fmt.Println(arr1 === nil) //true
切片的循环遍历
- for
var strSlice = []string{"php", "java", "nodejs", "golang"}
for i := 0; i < len(strSlice); i++{
fmt.Println(strSlice[i])
}
- for range
var strSlice = []string{"php", "java", "nodejs", "golang"}
for k, v := range strSlice {
fmt.Println(k, v)
}
}
基于数组定义切片
获取数组中的所有值
a := [5]int{1, 3, 5, 7, 9}
b := a[:]
fmt.Printf("%v-%T", b, b) //[1 3 5 7]-[]int
截取数组的某一部分[)——左包右不包
a := [5]int{1, 3, 5, 7, 9}
b := a[1:4]
c := a[3:]//获取第三个下标之后的数据
d := a[:3]//获取第三个下标之前的数据
fmt.Printf("%v-%T\n", b, b)//[3 5 7]-[]int
fmt.Printf("%v-%T", c, c)//[5, 7]-[]int
fmt.Printf("%v-%T", d, d)//[1 3 5]-[]int
切片再切片
除了基于数组得到切片,还可以通过切片来得到切片
a := []string{"北京","上海","广州","深圳","成都","重庆"}
b := a[1:]
fmt.Printf("%v-%T\n", b, b)//[上海 广州 深圳 成都 重庆]
切片的长度及容量
切片拥有自己的长度和容量,可以通过内置的
len()
函数求长度,cap()
求容量。
切片的本质就是对底层数组的封装,它包含了三个信息:底层数据的指针、切片的长度(len)和切片的容量(cap)
长度是它所包含的元素个数
容量是从它的第一个元素开始,到其底层数组元素末尾的个数
s := []int{1, 2, 3, 4, 5, 6}
fmt.Printf("长度%d 容量%d", len(s), cap(s))
//长度6 容量6
a := s[2:]
fmt.Printf("长度%d 容量%d", len(a), cap(a))
//长度4 容量4
b := s[1:3]
fmt.Printf("长度%d 容量%d", len(b), cap(b))
//长度2 容量5
c := s[:3]
fmt.Printf("长度%d 容量%d", len(c), cap(c))
//长度3 容量6
make()函数来构造切片
make([]T, size, cap)声明一个长度为size,容量为cap的切片
var sliceA = make([]int, 4, 8)
fmt.Println(sliceA)//[0 0 0 0]
fmt.Printf("%d-%d",len(sliceA), cap(sliceA))//4-8
切片的修改
var sliceA = make([]int, 4, 8)
sliceA[0] = 10
sliceA[1] = 12
fmt.Println(sliceA)//[10 12 0 0]
sliceB := []string{"js", "java", "go"}
sliceB[2] = "node"
fmt.Println(sliceB)//[js java node]
切片的扩容append
golang中不能通过下标的方式给切片扩容,需要用到append()方法,类似于js中的concat()
append()后面可以传多个参数
var sliceC []int
fmt.Printf("值%v-长度%v-容量%v",sliceC,len(sliceC),cap(sliceC))//值[]-长度0-容量0
sliceC = append(sliceC, 12)
fmt.Printf("值%v-长度%v-容量%v",sliceC,len(sliceC),cap(sliceC))//值[12]-长度1-容量1
append合并切片
sliceA := []string{"php","java"}
sliceB := []string{"node","go"}
sliceA = append(sliceA, sliceB...)
fmt.Println(sliceA)//[php java node go]
- 切片的扩容策略
- 首先判断,如果新申请容量大于2倍的旧容量,最终容量是新申请的容量
- 否则判断,如果旧切片的长度小于1024,则最终容量是旧容量的两倍;如果旧切片长度>=1024,则最终容量从旧容量开始循环增加原来的1/4,即(newcap = old.cap, for{newcap += newcap / 4}),直到最终容量大于新申请的容量
- 如果最终容量计算值溢出,则最终容量就是新申请容量
需要注意的是,切片扩容还会根据切片中元素的类型不同而做不同的处理,比如int和string类型的处理方式就不一样。
var sliceA []int
for i := 0; i < 5; i++{
sliceA = append(sliceA, i)
fmt.Printf("%v长度%d 容量%d", sliceA, len(sliceA), cap(sliceA))
}
//[1]长度1 容量1
//[1 2]长度2 容量2
//[1 2 3]长度3 容量4
//[1 2 3 4]长度4 容量4
//[1 2 3 4 5]长度5 容量8
可以通过查看
$GOROOT/src/runtime/slice.go
源码,其中扩容相关代码如下:
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
}else {
if old.len < 1024{
newcap = doublecap
} else {
for 0 < newcap && newcap < cap {
newcap += newcap / 4
}
if newcap <= 0 {
newcap = cap
}
}
}
使用copy()函数复制切片
切片是引用数据类型
sliceA := []int{1, 2, 3}
sliceB := sliceA
sliceB[0] = 11
fmt.Println(sliceA)//[11 2 3]
fmt.Println(sliceB)//[11 2 3]
使用copy复制切片(浅拷贝)
sliceA := []int{1, 2, 3}
sliceB := make([]int, 4, 4)
copy(sliceB, sliceA)//把A复制到B中
sliceA[0] = 11
fmt.Println(sliceA)//[11 2 3]
fmt.Println(sliceB)//[1 2 3]
从切片中删除元素
Go语言中并没有删除切片元素的专用方法,可以使用切片本身的特性来删除元素
a := []int{1,2,3,4}
//要删除索引为2的元素(3)
a = append(a[:2],a[3:]...)
fmt.Println(a)//[1 2 4]
注意:append合并切片时,最后一个元素后面加
...
有疑问加站长微信联系(非本文作者)