「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战」
写在前面👀
今天主要讲讲Go的切片🍱
一、什么是切片(Slice)🍘
切片(Slice)是同一种数据类型的长度不固定的序列🍙
- 虽然切片很像数组,但它并不是数组或数组指针,但他们两者的关系又很密切
- 和数组不同的是,切片不需要说明长度
- 切片是一个引用类型,是对数组的一个连续片段的引用
- 可以简单理解为切片是动态的,数组是静态的,一般来说切片比数组更常用
- 切片的构成:指针、长度、容量
切片组成 | 描述 |
| ---- | ---------------------------------------- |
| 指针 | 指向第一个slice元素对应的底层数组元素的地址 |
| 长度 | slice中元素的数目,长度不能超过容量,用
len()函数返回切片长度 | | 容量 | 一般是从slice的开始位置到底层数据的结尾位置,用cap()函数返回切片的容量 |
二、切片的创建与初始化🍚
1.直接创建新的切片🍤
- 语法👇
声明切片:
var 切片名 []Type //[]中括号里不需要填,Type表示基本类型
var a []int //值为nil
var b []string //值为nil
全局初始化:
var c = []int{} //空切片,值不为nil
var d = []int{1, 2, 3, 4}
局部初始化:
e := []string{"a", "b", "c", "d"}
- 示例👇
package main
import "fmt"
var a []int //值为nil
var b []string //值为nil
var c = []int{} //空切片,值不为nil
var d = []int{1, 2, 3, 4}
func main() {
e := []string{"a", "b", "c", "d"}
fmt.Println(a, b, c, d, e)
fmt.Println(a==nil)
fmt.Println(b==nil)
fmt.Println(c==nil)
}
- 结果👇
2.通过数组或切片创建新切片🍛
切片默认指向一段连续内存区域,可以是数组,也可以是切片本身🍜
-
语法👇 s表示的是数组或切片🦪 |操作 | 结果 | | ------------ | ------------------------------------------- | | s[start:end] | 截取的范围是索引start<=index<end,长度为end-start | | s[start:] | 截取的范围是索引start<=index<len(s),长度为len(s)-statt | | s[:end] | 截取的范围是索引0<=index<end,长度为end | | s[:] | 截取的范围是s本身,长度为len(s)| |s[0:0]|等效于空切片,一般用于切片复位|
-
示例👇
package main
import "fmt"
var s1 = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} //创建一个int型数组
/*通过数组创建切片*/
var s2 []int = s1[1:5] //全局变量
var s3 = s1[1:] //也可以不写[]int
func main() {
/*局部变量*/
s4 := s1[:9]
s5 := s1[:]
s6 := s1[0:0]
/*输出*/
fmt.Printf("s1 type:%T value:%v len:%d cap:%d\n", s1, s1, len(s1), cap(s1))
fmt.Printf("s2 type:%T value:%v len:%d cap:%d\n", s2, s2, len(s2), cap(s2))
fmt.Printf("s3 type:%T value:%v len:%d cap:%d\n", s3, s3, len(s3), cap(s3))
fmt.Printf("s4 type:%T value:%v len:%d cap:%d\n", s4, s4, len(s4), cap(s4))
fmt.Printf("s5 type:%T value:%v len:%d cap:%d\n", s5, s5, len(s5), cap(s5))
fmt.Printf("s6 type:%T value:%v len:%d cap:%d\n", s6, s6, len(s6), cap(s6))
}
- 结果👇
- 注意点❗
- 可以发现s1和s5输出的格式、长度、容量都一样,但他们的类型是不同的!s1是数组,s5是切片
- s3和s4明明长度相同,元素个数也一样,但他们的容量不相同。这是因为容量是从start位置(即
冒号:左边)开始算起的 - s6是空切片,长度为零,但系统已经为它分配了内存
3.使用make()函数初始化切片🍣
- 语法👇
make([]Type,len,cap) //Type表示类型,len表示要生成的长度,cap(为可选参数)表示容量
- 示例👇
make()函数的作用是初始化切片(初始化切片的长度和容量),初始化以后就有了内存空间
/*错误做法❗❗❗*/
package main
import "fmt"
var a []int //声明一个切片,但未初始化,是nil切片
func main() {
a[0] = 1 //nil切片没有内存,不能直接赋值
fmt.Println(a)
}
/*正确做法✅*/
package main
import "fmt"
var a []int //声明一个切片,但未初始化,是nil切片
func main() {
a = make([]int, 5,10) //使用make()初始化切片的长度和容量
a[0] = 1 //分配了内存后不为nil,可以直接赋值,且未赋值的为默认值0
fmt.Println(a)
}
- 结果👇
- 错误结果❌
编译器会报错!
- 正确结果✔
三、用for range遍历切片🍥
- 示例👇
package main
import (
"fmt"
)
func main() {
s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
for index, value := range s {
fmt.Printf("inde : %v , value : %v\n", index, value)
}
}
- 结果👇
四、用append()为切片追加元素🥮
1.基本用法🍢
为S1增加元素:s1:=append(S1,element)
- 示例👇
package main
import "fmt"
var a []int
var b = []int{5, 6, 7}
func main() {
a = append(a, 1) // 为a追加1个元素
a = append(a, 2, 3, 4) // 为a追加多个元素
a = append(a, b...) // 为a追加一个切片, 切片需要用...解包
fmt.Println(a)
}
- 结果👇
2.注意点🥘
- 切片容量充足,在原切片基础上追加,无内存 op。
- 切片容量不足,会分配新的切片空间,有内存 op。性能相对低。
- 扩容规律按切片容量的 2 倍进行扩容
- 示例👇
package main
import "fmt"
var a []int
func main() {
for i := 0; i < 10; i++ {
a = append(a, i)
fmt.Printf("len:%d cap:%d pointer:%p value:%v\n", len(a), cap(a), a, a)
}
}
- 结果👇
3. 向指定位置插入元素🧆
- 用for range和append做🍲
- 示例👇
package main
import "fmt"
/*posion表示插入的位置,source表示目标切片,target表示待插入切片*/
func SliceInsert(source []int, posion int, target []int) []int {
result := []int{}
for k, v := range source {
result = append(result, v)
if k == posion-1 {
result = append(result, target...)
}
}
return result
}
func main() {
source := []int{1, 4, 5, 6}
target := []int{2, 3}
fmt.Println(SliceInsert(source, 1, target))
}
- 结果👇
2. 用两个append做🍝
- 示例👇
package main
import "fmt"
/*posion表示插入的位置,source表示目标切片,target表示待插入切片*/
func SliceInsert(source []int, posion int, target []int) []int {
source = append(source[:posion], append(target, source[posion:]...)...)
return source
}
func main() {
source := []int{1, 4, 5, 6}
target := []int{2, 3}
fmt.Println(SliceInsert(source, 1, target))
}
- 结果👇
4. 删除索引为i的元素🥣
s2:=append(s1[:i], s1[i+1:]...)
5.删除索引i-j的元素🥧
s2 := append(append([]int{}, s1[:i]...), s1[j:]...)
五、用copy()复制一个切片到另一个切片🍦
copy(s2,s1) //把s1的内容复制到s2中去,要注意s2的长度要足够接受s1,才能完整复制
- 示例👇
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3}
s2 := make([]int, 1)
s3 := make([]int, 3)
copy(s2, s1) //s2长度不够,不能完整复制
copy(s3, s1) //s3长度够,能完整复制
fmt.Println("s1:", s1)
fmt.Println("s2:", s2)
fmt.Println("s3:", s3)
}
- 结果👇
六、其他🍧
- 切片能和其他类型结合起来用,如mapslice、slicemap、结构体切片等等,我会在后面的文章提及到🍨
写在后面🙌
感谢观看✨
如有不足,欢迎指出💖