【Go语言基础】slice,没那么简单

一、概述#

数组(Array)的长度在定义之后无法再次修改;数组是值类型,每次传递都将产生一份副本。

显然这种数据结构无法完全满足开发者的真实需求。Go语言提供了数组切片(slice)来弥补数组的不足。

切片并不是数组或数组指针,它通过内部指针和相关属性引⽤数组⽚段,以实现变⻓⽅案。

slice并不是真正意义上的动态数组,而是一个引用类型。slice总是指向一个底层array,slice的声明也可以像array一样,只是不需要长度。

二、基本语法#

切片的创建和初始化#

slice和数组的区别:声明数组时,方括号内写明了数组的长度或使用...自动计算长度,而声明slice时,方括号内没有任何字符。

Copy Highlighter-hljs
var s1 []int //声明切片和声明array一样,只是少了长度,此为空(nil)切片 s2 := []int{} //make([]T, length, capacity) //capacity省略,则和length的值相同 var s3 []int = make([]int, 0) s4 := make([]int, 0, 0) s5 := []int{1, 2, 3} //创建切片并初始化

注意:make只能创建slice、map和channel,并且返回一个有初始值(非零)。

Copy Highlighter-hljs
// runtime/slice.go type slice struct { array unsafe.Pointer // 元素指针 len int // 长度 cap int // 容量 }

【引申1】[3]int 和 [4]int 是同一个类型吗?

不是。因为数组的长度是类型的一部分,这是与 slice 不同的一点。

cap和len的区别#

简单点说,len(sli)表示可见元素有几个(即直接打印元素看到的元素个数),而cap(sli)表示所有元素有几个。

比如:

Copy Highlighter-hljs
arr := []int{2, 3, 5, 7, 11, 13} sli := arr[1:4] fmt.Println(sli) //[3 5 7] fmt.Println(len(sli))//3 fmt.Println(cap(sli))//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)

示例说明:#

array := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

操作 结果 len cap 说明
array[:6:8] [0 1 2 3 4 5] 6 8 省略 low
array[5:] [5 6 7 8 9] 5 5 省略 high、 max
array[:3] [0 1 2] 3 10 省略 high、 max
array[:] [0 1 2 3 4 5 6 7 8 9] 10 10 全部省略

append#

append函数向 slice 尾部添加数据,返回新的 slice 对象:

Copy Highlighter-hljs
var s1 []int //创建nil切换 //s1 := make([]int, 0) s1 = append(s1, 1) //追加1个元素 s1 = append(s1, 2, 3) //追加2个元素 s1 = append(s1, 4, 5, 6) //追加3个元素 fmt.Println(s1) //[1 2 3 4 5 6] s2 := make([]int, 5) s2 = append(s2, 6) fmt.Println(s2) //[0 0 0 0 0 6] s3 := []int{1, 2, 3} s3 = append(s3, 4, 5) fmt.Println(s3)//[1 2 3 4 5]

append函数会智能地底层数组的容量增长,一旦超过原底层数组容量,通常以2倍容量重新分配底层数组,并复制原来的数据:

Copy Highlighter-hljs
func main() { s := make([]int, 0, 1) c := cap(s) for i := 0; i < 50; i++ { s = append(s, i) if n := cap(s); n > c { fmt.Printf("cap: %d -> %d\n", c, n) c = n } } /* cap: 1 -> 2 cap: 2 -> 4 cap: 4 -> 8 cap: 8 -> 16 cap: 16 -> 32 cap: 32 -> 64 */ }

copy#

函数 copy 在两个 slice 间复制数据,复制⻓度以 len 小的为准,两个 slice 可指向同⼀底层数组。

Copy Highlighter-hljs
data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} s1 := data[8:] //{8, 9} s2 := data[:5] //{0, 1, 2, 3, 4} copy(s2, s1) // dst:s2, src:s1 fmt.Println(s2) //[8 9 2 3 4] fmt.Println(data) //[8 9 2 3 4 5 6 7 8 9] 注意要目的方需要先分配好空间,不然会丢失。 比如 源有5个,目的3个,则只会拷贝3个过来。 g.textConfigsCopy = make([]string, len(g.textConfigs)) copy(g.textConfigsCopy, g.textConfigs)

切片删除元素#

Copy Highlighter-hljs
delete_index := 2 //查看删除位置之前的元素和 之后的元素 fmt.Println(slice[:delete_index], slice[delete_index+1:]) //将删除点前后的元素连接起来 slice = append(slice[:delete_index], slice[delete_index+1:]...) fmt.Println(slice)

四、slice对底层数组的修改#

Copy Highlighter-hljs
package main import "fmt" func main() { slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} s1 := slice[2:5] fmt.Println(s1) //[2 3 20] s2 := s1[2:6:7] // 4,5,6,7 s2 = append(s2, 100) s2 = append(s2, 200) fmt.Println(s2) //[4 5 6 7 100 200] s1[2] = 20//0, 1, 2, 3(赋值), 4, 5, 6, 7, 8, 9 fmt.Println(slice) //[0 1 2 3 20 5 6 7 100 9] //问题1:100怎么来的。 //问题2:为什么100后面是9不是200 }

打印一下看看#

Copy Highlighter-hljs
package main import "fmt" func main() { slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} fmt.Println(len(slice), cap(slice)) //10 10 s1 := slice[2:5] fmt.Println(s1) //[2 3 20] fmt.Println(len(s1), cap(s1)) //3 8 s2 := s1[2:6:7] // 4,5,6,7 fmt.Println(len(s2), cap(s2)) // 4 5 s2 = append(s2, 100) s2 = append(s2, 200) fmt.Println(s2) //[4 5 6 7 100 200] //0, 1, 2, 3(赋值), 4, 5, 6, 7, 8, 9 s1[2] = 20 fmt.Println(slice) //[0 1 2 3 20 5 6 7 100 9] }

100怎么来的:#

100后面为什么是9?#

s2扩容了。

posted @   快乐的提千万  阅读(96)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
点击右上角即可分享
微信分享提示
CONTENTS