go语言学习日记—切片
前言
11月份事情有点多,三四个比赛还有作业,所以暂停了一波,今天开始继续go的旅程
为什么会有切片?
切片在某种程度上和数组是很相似的,但为什么会有切片呢?主要的原因是数组的限制太多了,一旦数组的长度给定了,就无法改变,这就大大限制了灵活性。切片的诞生正是为了解决这个问题,切片的长度是可以变的,但切片的底层还是数组。这一点后面再说。
切片的定义
切片(slice)是一个拥有相同元素的可变长的序列。它是基于数组类型做的一层封装,非常灵活,支持自动扩容。切片是一个引用类型,它内部结构包含地址,长度,和容量。
var s1 []int // 定义一个存放int类型元素的切片
var s2 []string // 定义一个存放string类型元素的切片
这里[]里面没有放数字和...所以不是数组,这是切片定义和数组定义的划分
fmt.Println(s1, s2) // [] []
// len(s1):0 cap(s1):0
fmt.Printf("len(s1):%d cap(s1):%d\n",len(s1),cap(s1))
// len(s2):0 cap(s1):0
fmt.Printf("len(s2):%d cap(s1):%d\n",len(s2),cap(s2))
fmt.Println(s1 == nil) // true
fmt.Println(s2 == nil) // truefmt.Println(s1, s2) // [] []
fmt.Println(s1 == nil) // true
fmt.Println(s2 == nil) // true
此时因为我们还没有给切片进行初始化,所以切片的长度和容量都为0。
func len
func len(v Type) int内建函数len返回 v 的长度,这取决于具体类型:
数组:v中元素的数量 数组指针:*v中元素的数量(v为nil时panic) 切片、映射:v中元素的数量;若v为nil,len(v)即为零 字符串:v中字节的数量 通道:通道缓存中队列(未读取)元素的数量;若v为 nil,len(v)即为零
func cap
func cap(v Type) int内建函数cap返回 v 的容量,这取决于具体类型:
数组:v中元素的数量,与 len(v) 相同 数组指针:*v中元素的数量,与len(v) 相同 切片:切片的容量(底层数组的长度);若 v为nil,cap(v) 即为零 信道:按照元素的单元,相应信道缓存的容量;若v为nil,cap(v)即为零
切片的初始化
切片的初始化可以直接赋值,也可以通过数组经行切片。
1.直接赋值初始化
// 初始化
s1 = []int{1, 2, 3}
s2 = []string{"wuhan", "beijing", "shanghai"}
fmt.Println(s1, s2) // [1 2 3] [wuhan beijing shanghai]
fmt.Println(s1 == nil) // false
fmt.Println(s2 == nil) // false
// 长度和容量
// len(s1):3,len(s2):3
fmt.Printf("len(s1):%d,len(s2):%d\n", len(s1), len(s2))
// cap(s1):3,cap(s2):3
fmt.Printf("len(s2):%d,cap(s2):%d\n", len(s2), cap(s2))
通过直接赋值初始化,切片的长度和容量都是你所赋值的数据的长度,这就相当直接初始化数组了。那还用切片干啥。所以这种方式用的不是很多。
2.通过数组初始化
a1 := [...]int{1, 3, 5, 7, 9, 11, 13}
s3 := a1[0:4] // 基于一个数组切割 左包含 右不包含
fmt.Println(s3)
s4 := a1[1:6]
fmt.Println(s4)
s5 := a1[:4]
fmt.Println(s5)
s6 := a1[3:]
fmt.Println(s6)
s7 := a1[:]
fmt.Println(s7)
// 切片的容量为底层数组从切片的第一个元素到最后的元素的数量
fmt.Printf("len(s6):%d,cap(s6):%d", len(s6), cap(s6))
[1 3 5 7] [3 5 7 9 11] [1 3 5 7] [7 9 11 13] [1 3 5 7 9 11 13] len(s6):4,cap(s6):4
这种方式,相信了解过python的肯定很熟悉了,这里我也不细说了。通过对数组切片的到数组,长度好说,就是这个切片里面元素的个数。容量就需要好好说一下了。
切片的容量为底层数组从切片的第一个元素到最后的元素的数量.
也就从你切片的头到数组的尾。因为切边本质上还是一个数组。
改变底层数组的值,切片的值也会发生改变,切片是引用数据类型
3.make()函数创建切片
func make
func make(Type, size IntegerType) Type内建函数make分配并初始化一个类型为切片、映射、或通道的对象。其第一个实参为类型,而非值。make的返回类型与其参数相同,而非指向它的指针。其具体结果取决于具体的类型:
切片:size指定了其长度。该切片的容量等于其长度。切片支持第二个整数实参可用来指定不同的容量; 它必须不小于其长度,因此 make([]int, 0, 10) 会分配一个长度为0,容量为10的切片。 映射:初始分配的创建取决于size,但产生的映射长度为0。size可以省略,这种情况下就会分配一个 小的起始大小。 通道:通道的缓存根据指定的缓存容量初始化。若 size为零或被省略,该信道即为无缓存的。
make()函数创建切片需要指定三个属性 1.切片里面的数据类型 2.切片的长度(默认为0) 3.切片的容量
// s1=[0 0 0 0 0] len(s1):5 cap(s1):10
s1 := make([]int, 5, 10)
fmt.Printf("s1=%v len(s1):%d cap(s1):%d\n", s1, len(s1), cap(s1))
4.切片的遍历
切片的遍历有两种方式,1.通过for循环遍历 2.通过for range遍历
1.通过for循环遍历
// 1.索引遍历
for i := 0; i < len(s1); i++ {
fmt.Println(s1[i])
}
2.通过for range遍历
// 2.for range循环
for i, v := range s1 {
fmt.Println(i, v)
}
s1=[0 0 0 0 0] len(s1):5 cap(s1):10 s2=[] len(s2):0 cap(s2):10 0 0 0 0 0 0 0 1 0 2 0 3 0 4 0