13、切片
1.切片的定义
Go 语言切片是对数组的抽象
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增
切片是一种方便、灵活且强大的包装器。切片本身没有任何数据。它们只是对现有数组的引用。
切片与数组相比,不需要设定长度,在口中不用设定值,相对来说比较自由
从概念上面来说slice像一个结构体,这个结构体包含了三个元素:
- 1.指针,指向数组中slice指定的开始位置
- 2.长度,即slice的长度
- 3.最大长度,也就是slice开始位置到数组的最后位置的长度
2.语法格式
var variable_name [] variable_type
可以发现切片和数组的语法格式很想,但是不需要指定大小的,也可以称之为:动态数组
例如:
var s1 []int
fmt.Println(s1)
s2 := []int{1, 2, 3, 4}
fmt.Println(s2)
fmt.Printf("s1的类型:%T", s1)
3.使用make(t Type,size ...IntegerType) Type
内置函数创建切片
/**
使用make内置函数
- 第一个参数:类型
- slice
- map
- chan
- 第二个参数:长度len
- 实际存储的元素
- 第三个参数:容量cap
能够存储的最多元素数量
*/
s3 := make([]int, 0, 5)
fmt.Println(s3)
4.append内置函数操作切片中的元素
// 使用append内置函数向切片末尾添加元素
s3 = append(s3, 1, 2, 3)
fmt.Println(s3)
// fmt.Println(s3[4]) // index out of range [4] with length 3
/**
超过容量了也没事,会自动扩容
扩容后会改变底层数组的引用地址
*/
s3 = append(s3, 4, 5, 6)
fmt.Println(s3)
fmt.Println("扩容", len(s3))
// 也可以把另外一个切append到当前切片
s3 = append(s3, s2...)
fmt.Println(s3)
5.数组的扩容机制
切片Slice:
- 1.每一个切片引用了一个底层数组
- 2.切片本身不存储任何数据,都是这个底层数组存储,所以修改切片也就是修改这个数组中的数据
- 3.当向切片中添加数据时,如果没有超过容量,直接添加,如果超过容量,自动扩容(成倍增长)
- 4.切片一旦扩容,就是重新指向一个新的底层数组
s1 := []int{1, 2, 3}
fmt.Println(s1)
fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1))
fmt.Printf("%p\n", s1)
fmt.Printf("%p\n", &s1)
s1 = append(s1, 4, 5)
fmt.Println(s1)
fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1))
fmt.Printf("%p\n", s1)
fmt.Printf("%p\n", &s1)
s1 = append(s1, 6, 7, 8)
fmt.Println(s1)
fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1))
fmt.Printf("%p\n", s1)
fmt.Printf("%p\n", &s1)
s1 = append(s1, 9, 10)
fmt.Println(s1)
fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1))
fmt.Printf("%p\n", s1)
fmt.Printf("%p\n", &s1)
6.在已有的数组上创建切片
slice := arr[start:end]
- 切片中的数据:[start,end)
- arr[:end],从头到end
- arr[start:]从start到未尾
从已有的数组上,直接创建切片,该切片的底层数组就是当前的数组.
- 长度是从start到end切割的数据量。
- 但是容量从start到数组的末尾。
/**
* @author ly (个人博客:https://www.cnblogs.com/qbbit)
* @date 2023/4/3 20:56
* @tags 喜欢就去努力的争取
*/
package main
import "fmt"
func main() {
a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(a)
s1 := a[:5]
s2 := a[3:8]
s3 := a[5:]
s4 := a[:]
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println(s4)
// 输出长度和容量
fmt.Printf("s1 len:%d,cap:%d\n", len(s1), cap(s1))
fmt.Printf("s2 len:%d,cap:%d\n", len(s2), cap(s2))
fmt.Printf("s3 len:%d,cap:%d\n", len(s3), cap(s3))
fmt.Printf("s4 len:%d,cap:%d\n", len(s4), cap(s4))
// 打印内存地址
fmt.Printf("%p\n", s1)
fmt.Printf("%p\n", s2)
fmt.Printf("%p\n", s3)
fmt.Printf("%p\n", s4)
// 更改数组内容,响应的切片内容也会随之更改
a[4] = 1000
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println(s4)
// 更改切片内容,数组的内容也会随之更改
s3[3] = 888
fmt.Println(a)
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println(s4)
// 向切片中添加内容
s2 = append(s2, 111, 222, 333)
fmt.Println(a)
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println(s4)
}
7.切片是引用数据类型
按照类型来分:
- 基本类型:int,float,string,bool
- 复合类型: array,slice,map,struct,pointer, function,chan
按照特点来分:
- 值类型: int,float,string,bool,array传递的是数据副本
- 引用类型: Slice传递的地址,多个变量指向了同一块内存地址
所以: 切片是引用类型的数据,存储了底层数组的引用
/**
* @author ly (个人博客:https://www.cnblogs.com/qbbit)
* @date 2023/4/3 21:45
* @tags 喜欢就去努力的争取
*/
package main
import "fmt"
func main() {
// 数组:值类型
arr1 := [2]int{1, 2}
arr2 := arr1
fmt.Println("数组改变前:", "arr1", arr1, "arr2", arr2)
arr1[0] = 5
fmt.Println("数组改变后:", "arr1", arr1, "arr2", arr2)
// 切片:引用类型
s1 := []int{10, 20, 30}
s2 := s1
fmt.Println("切片改变前:", "s1", s1, "s2", s2)
s1[0] = 100
fmt.Println("切片改变后:", "s1", s1, "s2", s2)
// 打印内存地址
fmt.Printf("%p\n", &arr1)
fmt.Printf("%p\n", &arr2)
fmt.Printf("%p\n", s1) // 底层数组的内存地址
fmt.Printf("%p\n", s2)
fmt.Printf("%p\n", &s1) // 打印切片本身的内存地址
fmt.Printf("%p\n", &s2)
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构