文章目录
- 切片由三个核心属性构成: 指针:指向底层数组的起始位置; 长度(len):当前存储的元素个数; 容量(cap):底层数组从切片起始位置到末尾的元素总数。 // 示例:查看切片属性 s := make([]int, 3, 5) fmt.Printf(“长度:%d 容量:%d 指针地址:%pn”, len(s), cap(s), s) // 输出:长度:3 容量:5 指针地址:0xc0000181e0
- 直接初始化: s1 := []int{1, 2, 3} // 长度和容量均为3 基于数组截取: arr := [5]int{0, 1, 2, 3, 4} s2 := arr[1:4] // 元素为[1,2,3],len=3,cap=4(底层数组剩余空间) 通过make预分配: s3 := make([]int, 3, 5) // len=3,cap=5,初始值为[0,0,0]
- 当切片长度超过容量时,Go会触发自动扩容(策略:容量<1024时翻倍,≥1024时扩容25%): s := make([]int, 0, 2) for i := 0; i < 5; i++ { s = append(s, i) fmt.Printf(“追加%d → len:%d cap:%dn”, i, len(s), cap(s)) } /* 输出: 追加0 → len:1 cap:2 追加1 → len:2 cap:2 追加2 → len:3 cap:4 // 触发扩容 追加3 → len:4 cap:4 追加4 → len:5 cap:8 // 再次扩容 */
- 截取操作(如s[start:end])会共享底层数组,修改子切片可能影响原切片: original := []int{1, 2, 3, 4, 5} sub := original[1:3] // sub=[2,3],cap=4(原数组剩余空间) sub[0] = 99 fmt.Println(original) // 输出:[1 99 3 4 5]
- 复制切片:使用copy避免共享底层数组: src := []int{1, 2, 3} dst := make([]int, len(src)) copy(dst, src) // 完全独立的新切片 删除元素:通过append重组切片: s := []int{1, 2, 3, 4, 5} index := 2 // 删除索引2的元素(值3) s = append(s[:index], s[index+1:]…) fmt.Println(s) // 输出:[1 2 4 5]
- 频繁append会导致多次内存分配,建议预判容量: // 错误示范:未预分配,触发多次扩容 var data []int for i := 0; i < 1000; i++ { data = append(data, i) // 多次扩容影响性能 } // 正确做法:预分配足够容量 data := make([]int, 0, 1000) // 一次分配,避免扩容
- nil切片:未初始化的切片(var s []int),len和cap均为0; 空切片:已初始化但无元素(s := make([]int, 0)),可用于JSON序列化空数组。
- 内部切片长度可动态变化,适合处理不规则数据: matrix := make([][]int, 3) for i := range matrix { matrix[i] = make([]int, i+1) // 每行长度不同 } // 输出:[[0] [0 1] [0 1 2]]
目录
- 一、切片基础与创建方式
- 1.1 切片的底层结构
- 1.2 创建切片的三种方式
- 二、切片的常用操作
- 2.1 动态扩容与append
- 2.2 切片截取与共享陷阱
- 2.3 安全复制与删除元素
- 三、高级技巧与注意事项
- 3.1 预分配容量优化性能
- 3.2 nil切片 vs 空切片
- 3.3 多维切片
- 四、常见错误与规避
- 五、总结
切片由三个核心属性构成:
- 指针:指向底层数组的起始位置;
- 长度(len):当前存储的元素个数;
- 容量(cap):底层数组从切片起始位置到末尾的元素总数。
// 示例:查看切片属性
s := make([]int, 3, 5)
fmt.Printf("长度:%d 容量:%d 指针地址:%pn", len(s), cap(s), s)
// 输出:长度:3 容量:5 指针地址:0xc0000181e0
直接初始化:
s1 := []int{1, 2, 3} // 长度和容量均为3
基于数组截取:
arr := [5]int{0, 1, 2, 3, 4}
s2 := arr[1:4] // 元素为[1,2,3],len=3,cap=4(底层数组剩余空间)
通过make预分配:
s3 := make([]int, 3, 5) // len=3,cap=5,初始值为[0,0,0]
当切片长度超过容量时,Go会触发自动扩容(策略:容量<1024时翻倍,≥1024时扩容25%):
s := make([]int, 0, 2)
for i := 0; i < 5; i++ {
s = append(s, i)
fmt.Printf("追加%d → len:%d cap:%dn", i, len(s), cap(s))
}
/* 输出:
追加0 → len:1 cap:2
追加1 → len:2 cap:2
追加2 → len:3 cap:4 // 触发扩容
追加3 → len:4 cap:4
追加4 → len:5 cap:8 // 再次扩容
*/
截取操作(如s[start:end])会共享底层数组,修改子切片可能影响原切片:
original := []int{1, 2, 3, 4, 5}
sub := original[1:3] // sub=[2,3],cap=4(原数组剩余空间)
sub[0] = 99
fmt.Println(original) // 输出:[1 99 3 4 5]
复制切片:使用copy避免共享底层数组:
src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src) // 完全独立的新切片
删除元素:通过append重组切片:
s := []int{1, 2, 3, 4, 5}
index := 2 // 删除索引2的元素(值3)
s = append(s[:index], s[index+1:]...)
fmt.Println(s) // 输出:[1 2 4 5]
频繁append会导致多次内存分配,建议预判容量:
// 错误示范:未预分配,触发多次扩容
var data []int
for i := 0; i < 1000; i++ {
data = append(data, i) // 多次扩容影响性能
}
// 正确做法:预分配足够容量
data := make([]int, 0, 1000) // 一次分配,避免扩容
nil切片:未初始化的切片(var s []int),len和cap均为0;
- 空切片:已初始化但无元素(
s := make([]int, 0)),可用于JSON序列化空数组。
nil切片:未初始化的切片(var s []int),len和cap均为0;s := make([]int, 0)),可用于JSON序列化空数组。
内部切片长度可动态变化,适合处理不规则数据:
matrix := make([][]int, 3)
for i := range matrix {
matrix[i] = make([]int, i+1) // 每行长度不同
}
// 输出:[[0] [0 1] [0 1 2]]
越界访问:
s := []int{1, 2, 3}
fmt.Println(s[3]) // panic: runtime error
误用共享底层数组:
a := []int{1, 2, 3}
b := a[:2]
b[0] = 99 // 修改b会影响a
fmt.Println(a) // 输出:[99,2,3]
忽略append返回值:
s := make([]int, 2, 3) append(s, 4) // 错误!未接收新切片 s = append(s, 4) // 正确
切片是Go语言中处理动态集合的核心工具,使用时需注意:
- 理解底层数组共享机制,必要时使用
copy; - 预分配容量以减少扩容开销;
- 区分
nil切片与空切片的语义差异。
通过合理使用切片,可以编写出高效且易于维护的Go代码。更多底层实现细节可参考Go官方文档。
// 完整示例代码
package main
import "fmt"
func main() {
// 创建切片
s1 := []int{1, 2, 3}
s2 := make([]int, 2, 5)
// 动态扩容
for i := 0; i < 10; i++ {
s2 = append(s2, i)
fmt.Printf("len:%d cap:%dn", len(s2), cap(s2))
}
// 安全复制
s3 := make([]int, len(s1))
copy(s3, s1)
s3[0] = 99
fmt.Println("原切片未受影响:", s1) // [1 2 3]
// 多维切片
matrix := make([][]int, 3)
for i := range matrix {
matrix[i] = make([]int, i+1)
for j := 0; j <= i; j++ {
matrix[i][j] = i + j
}
}
fmt.Println("多维切片:", matrix) // [[0] [1 2] [2 3 4]]
}
以上就是Go语言切片(Slice)使用技巧与避坑指南的详细内容,更多关于Go切片使用与避坑的资料请关注风君子博客其它相关文章!
您可能感兴趣的文章:
- Go语言Slice切片底层的实现
- Go语言切片(Slice)深度剖析与应用实战
- Golang中slice切片的实现示例
- go语言切片slice使用细节和注意事项整理大全
- 深入了解Golang中Slice切片的使用