大家好,我是沐风晓月,本文go语言入门-掌握go语言函数收录于《go语言学习专栏》专栏,此专栏带你从零开始学习go语言,持续更新中,欢迎点赞收藏。
个人主页:我是沐风晓月
个人简介:大家好,我是沐风晓月,双一流院校计算机专业,阿里云博客专家
座右铭:先努力成长自己,再帮助更多的人,一起加油进步
欢迎大家:这里是CSDN,我总结知识的地方,喜欢的话请三连,有问题请私信
Go语言中的切片,是一种动态数组,它是对数组的引用,切片本身并不存储任何数据,它只是描述了数组中的一段。
切片有如下几个特点:
从概念上⾯来说slice像⼀个结构体,这个结构体包含了三个元素:
1)指针,指向数组中slice指定的开始位置;
2)长度,即slice的当前⻓度;
3)容量,也就是slice所容纳的最大元素个数。
切片是一种数据类型,这种数据类型便于使用和管理数据集合。
首先我们需要明白切片的结构。slice在Go的运行时库中就是一个C语言动态数组的实现:
type slice struct {
ptr *array //底层存储数组
len int //当前存储了多少个元素
cap int //底层数组可以存储多少个元素(从ptr指向的位置开始)
}
这个结构有3个字段,第一个字段表示array的指针,就是真实数据的指针(这个一定要注意),第二个是表示slice的长度,第三个是表示slice的容量,特别需要注意的是:
1.slice的长度和容量都不是指针
2.切片操作并不复制切片指向的元素。它创建一个新的切片并复用原来切片的底层数组。
切片操作并不复制切片指向的元素。它创建一个新的切片并复用原来切片的底层数组。 使得切片操作和数组索引一样高效。因此,通过一个新切片修改元素会影响到原始切片的对应元素。
切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)。
这 3 个字段分别是指向底层数组的指针、切片访问的元素的个数(即长度)和切片允许增长到的元素个数(即容量)。
1. 声明一个未指定长度的数组来定义切片
语法:
var slicename []type
需要注意的是: 该声明⽅式中未初始化的切⽚为空切⽚。该切⽚默认为 nil,⻓度为 0。
package main
import "fmt"
func main() {
var a []string //声明一个字符串切片
var b = []int{} // 声明一个整数切片并初始化
fmt.Println(a, b, a == nil) // 执行结果 [] [] true
}
2. 使用make()的方式声明切片
语法:
var slice1 []type = make([]type, len, cap)
//type 表示切片的元素类型
//len 表示切片中元素的数量
//cap 表示切片的最大容量
可进行缩写:
- var slice1 []type = make([]type, len)
- 可以简写为: slice1 := make([]type, len)
案例一:使用make直接定义切片
package main
import "fmt"
func main() {
var mufeng2 []int = make([]int, 5, 7)
var mufeng2 []int = make([]int, 5) // 缩写
fmt.Println(mufeng2)
}
//执行结果: [0 0 0 0 0]
案例二:使用make定义并获相应的len,cap
package main
import "fmt"
func main() {
var mufeng = make([]int, 1, 6)
fmt.Println(mufeng)
fmt.Println(len(mufeng), cap(mufeng), mufeng)
fmt.Printf("len=%d cap=%d,slice=%v\n", len(mufeng), cap(mufeng), mufeng)
}
执行结果:
[0]
1 6 [0]
len=1 cap=6,slice=[0]
len : 表示长度
cap: 表示容量
容量可以省略不写,若不写默认与len相同。
使用make() 定义的时候,系统会初始化为0,而不是nil
。之所以 不是nil
,是因为make 函数
为其 分配了内存空间。
1. 直接初始化切片
package main
import "fmt"
func main() {
mufeng := []int{1, 2, 3} //由系统自动创建底层数组
fmt.Println(mufeng)
}
//执行结果: [1 2 3]
2. 基于数组来截取初始化切片
package main
import "fmt"
func main() {
//定义数组:
mufeng := [5]int{1, 2, 3, 4, 5}
//定义切片:
s := mufeng[:] //切片中包含数组的所有数据
s1 := mufeng[2:4]
s2 := mufeng[1:] //从下标1到最后
s3 := mufeng[:3]// 从最开始到下标为3-1
fmt.Println(mufeng, s, s1, s2, s3)
//执行结果:
}
执行结果:
[1 2 3 4 5] [1 2 3 4 5] [3 4] [2 3 4 5] [1 2 3]
代码详解:
定义数组: mufeng := [5]int{1, 2, 3, 4, 5}
s := mufeng[:]
s := mufeng[startIndex:endIndex]
表示从startIndex 到endIndex :s1 := mufeng[2:4]
这里的[]
是一个前闭后开区间
比如这这个例子中,这里的startIndex
就是2,endIndex
就是4, 所以s1 := mufeng[2:4]
就是获取下标为2到4的值,结果就是[3, 4], 但这里不包含下标4
[2:4] == 2<=s1<4
总结:
操作 | 含义 |
---|---|
s[n] | 切片中索引位置为n的项 |
s[:] | 从数组索引为0的位置到len(s)-1的位置处所获得切片 |
s[n1:] | 从数组索引为n1的位置到len(s)-1处所获得的切片 |
s[:n2] | 从数组索引为0的位置到n2处所获得的切片 |
s[n1:n2:max] | 从索引n1位置到n2处所获得的切片。 |
len(s) | 切片s的长度 |
cap(s) | 切片s的容量 |
Go语言的内建函数 append() 可以为切片动态添加元素:
package main
import "fmt"
func main() {
var mufeng []int
mufeng = append(mufeng, 1) //追加一个元素
mufeng1 := append(mufeng, 2, 3, 4) //追加多个元素
mufeng2 := append(mufeng, []int{1, 2, 3}...) //追加一个切片
fmt.Println(mufeng, mufeng1, mufeng2)
}
输出结果:
[1] [1 2 3 4] [1 1 2 3]
这里需要注意: append() 在为切片去动态添加元素的时候,如果空间不足以容纳足够多的元素的时候,切片就会进行扩容。切片在扩容时,容量的扩展规律是按容量的 2 倍数进行扩充,例如 1、2、4、8、16等倍数扩容。
package main
import "fmt"
func main() {
var mufeng []int
for num := 0; num < 10; num++ {
mufeng = append(mufeng, num)
fmt.Printf("len:%d cap:%d,pointer:%p\n", len(mufeng), cap(mufeng), mufeng)
}
}
执行结果:
Go语言的内置函数 copy() 可以将一个数组切片复制到另一个数组切片中,如果加入的两个数组切片不一样大,就会按照其中较小的那个数组切片的元素个数进行复制。copy() 函数的使用格式如下:
copy( destSlice, srcSlice []T) int
// 其中 srcSlice 为数据来源切片
// destSlice 为复制的目标(也就是将 srcSlice 复制到 destSlice)
// 目标切片必须分配过空间且足够承载复制的元素个数,并且来源和目标的类型必须一致
// copy() 函数的返回值表示实际发生复制的元素个数。
案例一
package main
import "fmt"
func main() {
mufeng1 := []int{1, 2, 3, 4, 5}
mufeng2 := []int{5, 6, 7}
a := copy(mufeng1, mufeng2)
b := copy(mufeng2, mufeng1)
fmt.Println(a, b) //这里返回的是复制的总元素个数 3,3
fmt.Println(mufeng1, mufeng2) //[5 6 7 4 5] [5 6 7]
}
复制的流程: mufeng1 复制到mufeng2 :
mufeng2
前三个元素{5,6,7}
占位,只需要把mufeng1
的{4,5}
复制过来即可,也就是:{5,6,7,4,5}
Go语言并没有对删除切片元素提供专用的语法或者接口,需要使用切片本身的特性来删除元素,根据要删除元素的位置有三种情况,分别是从开头位置删除、从中间位置删除和从尾部删除,其中删除切片尾部的元素速度最快。
package main
import "fmt"
func main() {
mufeng1 := []int{1, 2, 3, 4, 5}
mufeng1 = mufeng1[1:] //删除开头的一个元素
fmt.Println(mufeng1) //[2 3 4 5]
mufeng1 = mufeng1[3:] //删除开头的三个元素
fmt.Println(mufeng1) //【5】
}
package main
import "fmt"
func main() {
mufeng1 := []int{1, 2, 3, 4, 5}
fmt.Println(mufeng1)
mufeng1 = append(mufeng1[:1], mufeng1[1+1:]...)
fmt.Println(mufeng1)
}
执行结果:
[1 2 3 4 5]
[1 3 4 5]
这里利用对 slice 的截取删除指定元素。注意删除时,后面的元素会前移,所以下标 i 应该左移一位。
上面的第二种方法也是这种方法,其实需要自己指定要删除的下标,接下来我们用for循环实现:
import "fmt"
func DeleteSlice(a []int, elem int) []int {
for i := 0; i < len(a); i++ {
if a[i] == elem {
a = append(a[:i], a[i+1:]...)
i--
}
}
return a
}
func main() {
x := DeleteSlice([]int{1, 2, 3, 4, 5}, 2)
fmt.Println(x) //[1 3 4 5]
}
这种方法修改了原来的切片: 切片是一个有三个字段的结构体,分别是: 地址,长度和容量, 这还总方法改变的是地址和长度,但是容量没有变:
package main
import "fmt"
func DeleteSlice(a []int, elem int) []int {
for i := 0; i < len(a); i++ {
if a[i] == elem {
a = append(a[:i], a[i+1:]...)
i--
}
}
return a
}
func main() {
p := []int{1, 2, 3, 4, 5}
fmt.Println(p, len(p), cap(p))
fmt.Printf("address of p %p", &p)
x := DeleteSlice(p, 2)
fmt.Println(x) //[1 3 4 5]
fmt.Println(len(x), cap(x))
fmt.Printf("address of x %p", &x)
}
执行结果:
C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_test_hello_go.exe
[1 2 3 4 5] 5 5
address of p 0xc000008078
[1 3 4 5]
4 5
address of x 0xc0000080a8
切片这边比较难,建议每隔一段时间就回来复习。
好啦,这就是今天要分享给大家的全部内容了,我们下期再见!
本文由沐风晓月原创,首发于CSDN博客, 博客主页:mufeng.blog.csdn.net
日拱一卒无尽有,功不唐捐终入海
喜欢的话记得点赞收藏哦