Golang切片是Go语言中基于数组封装的动态数据结构,相比数组拥有更灵活的长度调整能力,是日常开发中处理集合数据的首选类型。切片本身不存储数据,它只是底层数组的一个引用,这个特性决定了切片的很多行为逻辑。

Golang切片的基础语法
切片的声明与初始化
切片的声明有多种方式,最常见的是使用var关键字声明、使用字面量初始化以及通过make函数创建。
package main
import "fmt"
func main() {
// 声明一个整型切片,初始值为nil
var slice1 []int
fmt.Println(slice1) // 输出 []
// 使用字面量初始化切片
slice2 := []string{"a", "b", "c"}
fmt.Println(slice2) // 输出 [a b c]
// 使用make函数创建切片,第一个参数是类型,第二个是长度,第三个可选参数是容量
slice3 := make([]int, 3, 5)
fmt.Printf("长度: %d, 容量: %dn", len(slice3), cap(slice3)) // 输出 长度: 3, 容量: 5
}
切片的常用操作
切片支持截取、追加、复制等常用操作,这些操作都和切片的底层结构密切相关。
- 切片截取:通过
slice[low:high]的形式截取,low是起始索引(包含),high是结束索引(不包含),截取后得到的新切片和原切片共享底层数组。 - 元素追加:使用内置的
append函数向切片末尾添加元素,如果追加后长度超过容量,会触发扩容机制。 - 切片复制:使用内置的
copy函数将一个切片的元素复制到另一个切片,复制的长度取两个切片长度的最小值。
package main
import "fmt"
func main() {
baseSlice := []int{1, 2, 3, 4, 5}
// 截取切片,得到[2,3,4]
subSlice := baseSlice[1:4]
fmt.Println(subSlice) // 输出 [2 3 4]
// 追加元素
subSlice = append(subSlice, 6)
fmt.Println(subSlice) // 输出 [2 3 4 6]
fmt.Println(baseSlice) // 输出 [1 2 3 4 6],因为共享底层数组,修改subSlice会影响baseSlice
// 复制切片
targetSlice := make([]int, 3)
copyCount := copy(targetSlice, subSlice)
fmt.Printf("复制了%d个元素,目标切片: %vn", copyCount, targetSlice) // 输出 复制了3个元素,目标切片: [2 3 4]
}
Golang切片的底层原理
切片的底层结构
Go语言中切片的底层是一个结构体,包含三个核心字段:指向底层数组的指针、切片的长度、切片的容量。长度表示切片当前包含的元素个数,容量表示底层数组从切片起始位置到数组末尾的元素个数。
| 字段 | 含义 |
|---|---|
| ptr | 指向底层数组的指针,切片操作的元素都存储在底层数组中 |
| len | 切片的长度,即当前切片中元素的个数,通过len()函数获取 |
| cap | 切片的容量,即底层数组从切片第一个元素开始到数组末尾的元素总数,通过cap()函数获取 |
切片的扩容机制
当使用append函数向切片追加元素,且追加后的长度超过当前切片的容量时,Go运行时会触发切片的扩容逻辑。扩容的核心规则如下:
- 如果当前切片的容量小于256,扩容后的新容量会直接翻倍。
- 如果当前切片的容量大于等于256,新容量会按照当前容量的1.25倍逐步增长,直到满足需求。
- 扩容后会创建一个新的底层数组,将原切片的元素复制到新数组中,新切片会指向这个新的底层数组,和原切片不再共享数据。
package main
import "fmt"
func main() {
slice := make([]int, 0, 4)
fmt.Printf("初始长度: %d, 初始容量: %dn", len(slice), cap(slice)) // 输出 初始长度: 0, 初始容量: 4
// 追加元素到超过容量
slice = append(slice, 1, 2, 3, 4, 5)
fmt.Printf("追加后长度: %d, 追加后容量: %dn", len(slice), cap(slice)) // 输出 追加后长度: 5, 追加后容量: 8,容量翻倍
}
切片使用注意事项
由于切片是底层数组的引用,在使用时需要注意共享底层数组带来的数据修改问题,如果需要对切片数据进行独立修改,建议先通过copy函数复制一份新的切片。另外,切片的零值是nil,对nil切片使用append函数是合法的,Go会自动为其分配底层数组。
注意:不要将切片和数组混淆,数组是值类型,赋值和传参会复制整个数组,而切片是引用类型,赋值和传参只会复制切片的结构体,底层数组不会被复制。