知识点 | 关键概念 |
---|---|
切片声明 | var slice []int |
初始化切片 | slice := []int{1,2,3} |
make() 创建切片 | make([]int, len, cap) |
获取长度和容量 | len(slice), cap(slice) |
追加元素 | slice = append(slice, value) |
切片截取 | slice[start:end] (返回子切片) |
拷贝切片 | copy(dest, src) (不会共享底层数组) |
删除元素 | slice = append(slice[:index], slice[index+1:]...) |
nil 切片 vs 空切片 | nilSlice == nil ,len(emptySlice) == 0 |
切片扩容 | 超过 cap 后,自动扩容 |
1. 切片的基本概念
代码示例
package mainimport "fmt"func main() {// 声明切片var slice1 []intfmt.Println(slice1) // []// 直接初始化切片slice2 := []int{1, 2, 3, 4, 5}fmt.Println(slice2) // [1 2 3 4 5]
}
学习笔记
- 切片是一种动态数组,可以扩容,而数组长度固定。
- 切片本质上是对底层数组的引用,它存储的是 指向底层数组的指针、长度和容量。
- 切片可以通过
{}
直接初始化,不需要指定长度。
2. 使用 make()
创建切片
代码示例
package mainimport "fmt"func main() {// 创建一个长度为 5 的切片,默认值为 0slice1 := make([]int, 5)fmt.Println(slice1) // [0 0 0 0 0]// 创建一个长度为 7,容量为 10 的切片slice2 := make([]int, 7, 10)fmt.Println(slice2) // [0 0 0 0 0 0 0]fmt.Println("长度:", len(slice2)) // 7fmt.Println("容量:", cap(slice2)) // 10
}
学习笔记
make([]type, len, cap)
用于创建切片,其中:len
是切片的初始长度。cap
是切片的容量(可选)。
len()
获取切片长度,cap()
获取切片容量。- 切片长度 <= 容量,超过容量时会自动扩展。
3. 切片的 append()
操作
代码示例
package mainimport "fmt"func main() {slice := []string{"A", "B"}slice = append(slice, "C", "D") // 追加元素fmt.Println(slice) // [A B C D]
}
学习笔记
append()
用于向切片追加元素,如果超出容量,Go 会 自动扩容 并分配新的底层数组。
4. 切片的截取
代码示例
package mainimport "fmt"func main() {slice := []int{1, 2, 3, 4, 5, 6, 7}subSlice := slice[1:4] // 从索引 1 截取到索引 4(不包含 4)fmt.Println(subSlice) // [2 3 4]subSlice[1] = 100 // 修改 subSlice 会影响原切片fmt.Println(slice) // [1 2 100 4 5 6 7]
}
学习笔记
slice[start:end]
获取[start, end)
之间的元素(不包含end
)。- 修改子切片的值会影响原切片,因为它们共用相同的底层数组。
5. 使用 copy()
进行切片拷贝
代码示例
package mainimport "fmt"func main() {src := []int{1, 2, 3, 4, 5}dest := make([]int, len(src))copy(dest, src) // 复制 src 到 destfmt.Println(dest) // [1 2 3 4 5]src[0] = 100fmt.Println(src) // [100 2 3 4 5]fmt.Println(dest) // [1 2 3 4 5] (不受影响)
}
学习笔记
copy(dest, src)
用于复制切片,不会共享底层数组。- 修改
src
不会影响dest
,避免数据被误修改。
6. 切片的删除操作
代码示例
package mainimport "fmt"func main() {slice := []int{1, 2, 3, 4, 5}slice = append(slice[:2], slice[3:]...) // 删除索引 2 的元素fmt.Println(slice) // [1 2 4 5]
}
学习笔记
- Go 没有
remove()
方法,删除元素时需要使用append()
将前后部分拼接。 slice[:index] + slice[index+1:]
实现删除,需要...
进行解包。
7. nil
切片 vs 空切片
代码示例
package mainimport "fmt"func main() {var nilSlice []intemptySlice := []int{}if nilSlice == nil {fmt.Println("nilSlice 是 nil")}if len(emptySlice) == 0 {fmt.Println("emptySlice 是空切片")}
}
学习笔记
nil
切片是未分配内存的切片,值为nil
,常用于表示 “无数据”。- 空切片已经初始化,但
len() == 0
,可以安全使用append()
添加数据。
8. 切片扩容机制
代码示例
package mainimport "fmt"func main() {slice := []int{}fmt.Println("初始容量:", cap(slice))for i := 1; i <= 10; i++ {slice = append(slice, i)fmt.Printf("追加 %d 后,长度: %d, 容量: %d\n", i, len(slice), cap(slice))}
}
学习笔记
- 切片的容量按 2 倍增长:
- 当
len(slice) < cap(slice)
,append()
直接使用现有的底层数组。 - 当超出
cap(slice)
,Go 会 重新分配内存 并扩展容量。
- 当
一、go1.18 之前:
1.如果期望容量大于当前容量的两倍就会使用期望容量;
2.如果当前切片的长度小于 1024 就会将容量翻倍;
3.如果当前切片的长度大于 1024 就会每次增加 25% 的容量,直到新容量大于期望容量;
二、go1.18 之后:
1.如果期望容量大于当前容量的两倍就会使用期望容量;
2.如果当前切片的长度小于阈值(默认 256)就会将容量翻倍;
3.如果当前切片的长度大于等于阈值(默认 256),就会每次增加 25% 的容量,基准是 newcap + 3*threshold,直到新容量大于期望容量;
扩容机制笔记参考自:https://blog.csdn.net/qq_47831505/article/details/135540661