一、切片的定义和使用
package main
import "fmt"
func main() {
//数组定义 var 数组名 [元素个数] 数据类型
//切片定义 var 切片名 [] 数据类型
var s [] int
fmt.Println(s)
}
输出结果
[]
可以看到,我们在上面的程序中,将切片的定义和数组的定义做了对比,切片的定义时,不需要任何元素个数
当定义完成切片后,我们做打印,发现切片为空,因没有任何数据
package main
import "fmt"
func main() {
//数组定义 var 数组名 [元素个数] 数据类型
//切片定义 var 切片名 [] 数据类型
var s [] int
s[0] = 123 //这里直接给切片复制,会报错,因为切片的长度为零
fmt.Println(s)
}
输出结果:
panic: runtime error: index out of range
goroutine 1 [running]:
main.main()
D:/go_work/切片.go:9 +0x17
我们定义完切片后,直接通过切片下标进行赋值操作,会出现上述错误,因为这时定义的切片长度为0,不能直接赋值
我们通过下面的方法,进行切片的定义
package main
import "fmt"
func main() {
//make([] 数据类型, 切片长度)
s := make([] int , 5) //通过自动推导类型,创建一个切片,类型为int,长度为5
//通过下标为切片赋值
s[0] = 123
s[1] = 456
s[2] = 789
fmt.Println(s)
}
二、make方法,创建切片操作,输出结果:
[123 456 789 0 0]
我们在没有对切片的元素进行赋值时,该元素的结果为0,也会被打印。
通过len查看切片长度
//通过len查看切片的长度
fmt.Println(len(s))
结果:
5
是我们定义的长度。
三、切片的扩展:
package main
import "fmt"
func main() {
//make([] 数据类型, 切片长度)
s := make([] int , 5) //通过自动推导类型,创建一个切片,类型为int,长度为5
//通过下标为切片赋值
s[0] = 123
s[1] = 456
s[2] = 789
s[3] = 111
s[4] = 222
//s[5] = 333 //我们不能直接给切片进行,越界赋值,会直接报越界错误
//但是通过append,可以直接添加切片元素
s = append(s, 333)
fmt.Println(s)
}
输出结果:
[123 456 789 111 222 333]
我们可以看到,通过,s[5] = 333 这种方式,不能直接给切片进行扩展操作,如果想要给一个长度已满的切片进行赋值操作,则需要我们通过append方法,进行添加元素操作。
我们在看看,添加完后的切片长度:
fmt.Println(len(s))
结果为:6
也可以通过append,添加多个元素,需要在元素值后面用逗号隔开:
s = append(s, 333,444,555)
四、切片的遍历操作:
1、
package main
import "fmt"
func main() {
//make([] 数据类型, 切片长度)
s := make([] int , 5) //通过自动推导类型,创建一个切片,类型为int,长度为5
//通过下标为切片赋值
s[0] = 123
s[1] = 456
s[2] = 789
s[3] = 111
s[4] = 222
// 切片的遍历
for i:=0; i<len(s);i++{
fmt.Println(s[i])
}
}
结果:
123
456
789
111
222
通过循环遍历出切片的值
2、
for i,v:=range s{
fmt.Println(i,v)
}
结果:
0 123
1 456
2 789
3 111
4 222
五、切片初始化操作:
我们在不适用make创建切片的时候,可以直接对切片进行初始化操作。
package main
import "fmt"
func main() {
//这种创建切片的方式,跟创建数组类似
//不写元素个数的,叫切片,必须写元素个数的叫数组
//切片与数组都是连续的存储空间,切片可以扩展,元素不能扩展。
//切片在内存中存储的区域是堆区。数组的数据存储在内存中的栈区
var s [] int = []int{1,2,3,4,5}
fmt.Println(s)
}
结果:
[1 2 3 4 5]
六、切片的容量:
package main
import "fmt"
func main() {
var s [] int = []int{1,2,3,4,5}
fmt.Println(s)
//切片的长度
fmt.Println("长度 = ",len(s))
// 切片的容量
fmt.Println("容量 = ", cap(s))
}
结果:
[1 2 3 4 5]
长度 = 5
容量 = 5
可以看到,我们使用cap方法来获得切片容量大小
我们在s中,添加一个元素
package main
import "fmt"
func main() {
var s [] int = []int{1,2,3,4,5}
fmt.Println(s)
//添加元素:
s = append(s, 6,7,8,9)
//切片的长度
fmt.Println("长度 = ",len(s))
// 切片的容量
fmt.Println("容量 = ", cap(s))
//添加一个元素到s中
s = append(s, 10,11,12,13)
//切片的长度
fmt.Println("长度 = ",len(s))
// 切片的容量
fmt.Println("容量 = ", cap(s))
}
输出结果:
[1 2 3 4 5]
长度 = 9
容量 = 12
长度 = 13
容量 = 24
可以看到,容量一定是大于等于长度的,且容量的每次扩展是上一次容量的倍数。
当容量大小,超过当前容量,才会自动扩展。
如果整体数据,没有超过1024个字节,每次扩展为上一次的倍数,如果超过1024,则每次扩展是上次的四分之一。
举例:
package main
import "fmt"
func main() {
s := make([]int, 0 ,1) //容量为1
oldCap := cap(s)
for i:=0; i<20; i++{
s = append(s,i)
newCap:= cap(s)
if oldCap < newCap{
fmt.Printf("cap: %d========> %d\n", oldCap, newCap)
oldCap = newCap
}
}
}
输出结果:
cap: 1========> 2
cap: 2========> 4
cap: 4========> 8
cap: 8========> 16
cap: 16========> 32
通过上面这个例子,我们可以看到,是切片的扩容是通过2倍进行的
在看下,下面这个例子:
package main
import "fmt"
func main() {
s := make([]int, 0 ,1) //容量为1
oldCap := cap(s)
for i:=0; i<200000; i++{ //我们吧循环条件修改变大
s = append(s,i)
newCap:= cap(s)
if oldCap < newCap{
fmt.Printf("cap: %d========> %d\n", oldCap, newCap)
oldCap = newCap
}
}
}
结果:
cap: 512========> 1024 //两倍扩容
cap: 1024========> 1344 //不再是两倍扩容
cap: 1344========> 1696
cap: 1696========> 2368
cap: 2368========> 3072
cap: 3072========> 4096
cap: 4096========> 5120
cap: 5120========> 6816
cap: 6816========> 10240
cap: 10240========> 14336
cap: 14336========> 18432
cap: 18432========> 24576
cap: 24576========> 30720
cap: 30720========> 38912
cap: 38912========> 49152
cap: 49152========> 61440
cap: 61440========> 77824
cap: 77824========> 98304
cap: 98304========> 122880
cap: 122880========> 153600
cap: 153600========> 192512
cap: 192512========> 241664
当容量小于1024时是按照2倍容量扩容,当大于等于1024是不是按照2倍容量扩容。
七、切片的截取:
首先说一下切片的截取操作,所谓截取就是从切片中获取指定的数据。
我们通过如下程序给大家解释一下:
package main
import "fmt"
func main() {
//定义切片,并完成初始化
s:= []int {10,20,30,40,50}
// 从切片s中截取数据
slice:= s[0:3:5]
fmt.Println(slice)
}
结果:
[10 20 30]
s[0:3:5]是什么意思呢?
我们可以使用s[low:high:max]来表示
第一个数(low)表示下标的起点(从该位置开始截取),如果low取值为0表示从第一个元素开始截取,也就是对应的切片s中的10
第二个数(high)表示取到哪结束,也就是下标的终点(不包含该位置),3表示取出下标是0,1,2的数据(10,20,30),不包括下标为3的数据,那么也就是说取出的数据长度是3. 可以根据公式:3-0 计算(len=high-low),也就是第二个数减去第一个数,差就是数据长度。在这里可以将长度理解成取出的数据的个数。
第三个数用来计算容量,所谓容量:是指切片目前可容纳的最多元素个数。通过公式5-0计算(cap=max-low),也就是第三个数据减去第一个数。该案例中容量为5
关于切片的截取还有其它的操作,如下图所示:
操作 |
含义 |
s[n] |
切片s中索引位置为n的项 |
s[:] |
从切片s的索引位置0到len(s)-1处所获得的切片 |
s[low:] |
从切片s的索引位置low到len(s)-1处所获得的切片 |
s[:high] |
从切片s的索引位置0到high处所获得的切片,len=high |
s[low:high] |
从切片s的索引位置low到high处所获得的切片,len=high-low |
s[low:high:max] |
从切片s的索引位置low到high处所获得的切片,len=high-low,cap=max-low |
len(s) |
切片s的长度,总是<=cap(s) |
cap(s) |
切片s的容量,总是>=len(s) |
通过下面的例子,我们简单的演示一下:
1、
package main
import "fmt"
func main() {
//定义切片,并完成初始化
s:= []int {0,1,2,3,4,5,6,7,8,9}
// 从切片s中截取数据
slice:= s[:] //不指定容量和长度一样
fmt.Println("slice = ",slice)
fmt.Printf("len = %d, cap = %d\n", len(slice), cap(slice))
}
输出结果:
slice = [0 1 2 3 4 5 6 7 8 9]
len = 10, cap = 10
2、
package main
import "fmt"
func main() {
//定义切片,并完成初始化
s:= []int {0,1,2,3,4,5,6,7,8,9}
// 从切片s中截取数据
slice:= s[3:] //从下标3开始
fmt.Println("slice = ",slice)
fmt.Printf("len = %d, cap = %d\n", len(slice), cap(slice))
}
结果:
slice = [3 4 5 6 7 8 9]
len = 7, cap = 7
3、
package main
import "fmt"
func main() {
//定义切片,并完成初始化
s:= []int {0,1,2,3,4,5,6,7,8,9}
// 从切片s中截取数据
slice:= s[:6] //从下0开始,取6个元素,容量是10
fmt.Println("slice = ",slice)
fmt.Printf("len = %d, cap = %d\n", len(slice), cap(slice))
}
结果:
slice = [0 1 2 3 4 5]
len = 6, cap = 10
4、
package main
import "fmt"
func main() {
//定义切片,并完成初始化
s:= []int {0,1,2,3,4,5,6,7,8,9}
// 从切片s中截取数据
slice:= s[2:5] //从下2开始(包含该元素),取到下标5为止(不包含该元素),切片长度为3,容量是8,10-2
fmt.Println("slice = ",slice)
fmt.Printf("len = %d, cap = %d\n", len(slice), cap(slice))
}
结果:
slice = [2 3 4]
len = 3, cap = 8
八、copy函数使用
针对切片操作常用的方法除了append( )方法以外,还有copy方法.
基本语法:copy(切片1,切片2)
将第二个切片里面的元素,拷贝到第一个切片中。
下面通过一个案例,看一下该方法的使用:
package main
import "fmt"
func main() {
s1 := []int{1,2}
s2 := []int{4,5,6,7,8}
copy(s2,s1) //将s1中的元素,拷贝到s2中
fmt.Print(s2)
}
结果:
[1 2 6 7 8]
通过以上结果可以分析出,直接将1切片中两个元素拷贝到s2元素中相同的位置。而s2原有的元素备替换掉。
在看下面的:
package main
import "fmt"
func main() {
s1 := []int{1,2}
s2 := []int{4,5,6,7,8}
copy(s1,s2) //将s2中的元素,拷贝到s1中
fmt.Print(s1)
}
输出结果:
[4 5]
而,将s2拷贝到s1,则会吧s1中的元素替换掉。
今天就讲到这里。感谢各位阅读,欢迎点赞评论。谢谢!!