如果❤️我的文章有帮助,欢迎点赞、关注。这是对我继续技术创作最大的鼓励。更多往期文章在我的个人博客
虽然 Golang 中很少直接使用数组,但由于 切片 slice
是 数组 arrays
的一种动态实现,所以讲解切片 slice
之前,很有必要先搞清楚 数组 arrays
如何实现。
数组 array
数组的声明
数组 arrays
就是一个 固定类型、固定长度
的序列。数组的语法如下:
var 变量名 [数组长度] 数组元素类型 // var a [2]int
数组中每个元素都能够通过 索引下标来访问
,索引下标从 0 开始 到 数组长度 - 1
,内置函数 len() 获得数组内元素个数(即:数组长度)。
数组初始化时每个元素的值默认为元素类型对应的零值
,也可以声明直接赋值
var b [3]int = [3]int{1, 2, 3}
如果出事化时不清楚数组长度,可以是用 ...
表示数组长度根据初始化时元素个数决定
c := […]int{1, 2, 3}
var a [3]int // 定义长度为 3 的整数数组
fmt.Println(a[0]) // 打印第一个元素、最后一个元素
fmt.Println(a[len(a)-1])
var b [3]int = [3]int{1, 2, 3} // 声明时直接赋值
c := [...]int{1, 2, 3} // 数组长度根据初始化时元素个数决定
// 比较两个数组是否相等
fmt.Println(a == b, a == c, b == c) // "false false true"
// 遍历输出索引和元素
for i, v := range a {
fmt.Printf("索引%d - 元素%d\n", i, v)
}
切片 slices
切片 slice 作为一种引用类型指向底层的数组,是对数组中某个 连续片段的引用
。需要留意的是,终止索引下标值不包括在切片内。
相对于数组的固定类型、固定长度
,Slice 可以动态增长、变短,功能相对更灵活
切片的声明
从数组、切片的连续内存生成切片是常有操作,切片语法如下:
slice[开始位置 : 结束位置] // slice 被切片变量
注意:终止索引下标值不包括在切片内,举个例子:
func main() {
c := [...]int{0,1, 2, 3,4,5,6,7,8,9}
fmt.Println(c[0:7]) // [0 1 2 3 4 5 6]
fmt.Println(c[:3]) // [0 1 2]
}
切片的容量上限
创建切片 Slice
也会经常用到内建函数 make,语法如下:
func make([]Type, len, cap) []Type
需要注意的是第 3 个可选参数
的容量上限。
- 不指定
容量上限 cap
的话,默认会和原数组长度 len 相同。 - 指定容量上限,切片后不能通过 slices[:cap(slices)] 访问原数组内容
- cap 为切片
第一个元素
在原数组的索引值
与原数组长度
的差值的绝对值
s := make([]int, 5),`切片 s` 长度为 5,容量上限 5。
s2 := s[1:3]
切片时 golang 会新建一个 切片 s2
,底层的引用数据不动
。s2 通过更改指针指向、长度、容量来进行切片。这也是切片性能非常高的原因。
一个切片不能越过容量上限 cap 进行操作,因为从指针的角度,这就是越界进行非法访问。
切片追加元素
Golang 的内建函数 append() 可以给切片动态追加元素, 当切片的容量上限不够容纳元素是,切片会自动进行扩容
,此时切片长度会被改变。
切片扩容时,容量上限是有规律地按容量上限的 2 倍
(1、2、4、8)进行扩充,并将之前的元素复制进去。下面测试了下:
var numbers []int
for i := 0; i < 10; i++ {
numbers = append(numbers, i)
fmt.Printf("len: %d cap: %d pointer: %p\n", len(numbers), cap(numbers), numbers)
}
输出:
/**
len: 1 cap: 1 pointer: 0xc0420080e8
len: 2 cap: 2 pointer: 0xc042008150
len: 3 cap: 4 pointer: 0xc04200e320
len: 4 cap: 4 pointer: 0xc04200e320
len: 5 cap: 8 pointer: 0xc04200c200
**/
如果要追加一个 slice 到另一个 slice 的话,这样:
s1 := make([]int, 5)
s2 := []int{1, 2, 3, 4, 5, 6}
s := append(s1, s2...)
fmt.Println(s, len(s), cap(s))//输出[0 0 0 0 0 1 2 3 4 5 6] 11 12
切片复制
Golang 的内置函数 copy() 可以复制数组切片,使用语法如下:
copy(number1,number2) //拷贝 number2 的内容到 number1
需要注意的是:如果复制时两个切片容量上限并不一致,会按照容量上限较小
的数组切片的容量上限
进行复制。
slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中
fmt.Println(slice2) // [1 2 3]
copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置
fmt.Println(slice1) // [1 2 3 4 5]