当前位置: 首页 > news >正文

Go语言-->切片

Go 语言切片详细解释

1. 切片的基本概念

1.1 什么是切片

切片是 Go 语言中的一种动态数组,它是对底层数组的一个连续片段的引用。切片比数组更灵活,是 Go 中最常用的数据结构之一。

1.2 切片的内部结构

// Go 切片的内部结构(简化版)
type slice struct {array unsafe.Pointer  // 指向底层数组的指针len   int             // 当前长度cap   int             // 容量
}

2. 切片的声明和初始化

2.1 基本声明方式

// 1. 声明一个空切片
var s []int                    // nil 切片,len=0, cap=0// 2. 使用 make 创建切片
s1 := make([]int, 5)          // len=5, cap=5, 元素都是零值
s2 := make([]int, 3, 10)      // len=3, cap=10// 3. 切片字面量
s3 := []int{1, 2, 3, 4, 5}    // len=5, cap=5// 4. 从数组创建切片
arr := [5]int{1, 2, 3, 4, 5}
s4 := arr[1:4]                // [2, 3, 4], len=3, cap=4

3. 切片的基本操作

3.1 访问和修改元素

s := []int{1, 2, 3, 4, 5}// 访问元素
fmt.Println(s[0])    // 1
fmt.Println(s[2])    // 3// 修改元素
s[1] = 10
fmt.Println(s)       // [1, 10, 3, 4, 5]// 获取长度和容量
fmt.Println(len(s))  // 5
fmt.Println(cap(s))  // 5

3.2 切片操作

s := []int{1, 2, 3, 4, 5}// 切片操作 [start:end]
s1 := s[1:3]         // [2, 3]
s2 := s[:3]          // [1, 2, 3]
s3 := s[2:]          // [3, 4, 5]
s4 := s[:]           // [1, 2, 3, 4, 5] (完整拷贝)

4. 切片的动态操作

4.1 append 操作

// append 是切片最重要的操作
s := []int{1, 2, 3}
fmt.Printf("原始: len=%d, cap=%d, %v\n", len(s), cap(s), s)// 添加单个元素
s = append(s, 4)
fmt.Printf("添加4: len=%d, cap=%d, %v\n", len(s), cap(s), s)// 添加多个元素
s = append(s, 5, 6, 7)
fmt.Printf("添加5,6,7: len=%d, cap=%d, %v\n", len(s), cap(s), s)// 合并切片
s2 := []int{8, 9, 10}
s = append(s, s2...)  // 注意三个点
fmt.Printf("合并切片: len=%d, cap=%d, %v\n", len(s), cap(s), s)

4.2 copy 操作

// copy 用于复制切片元素
src := []int{1, 2, 3, 4, 5}
dst := make([]int, 3)// 复制元素,返回实际复制的元素数量
n := copy(dst, src)
fmt.Printf("复制了 %d 个元素: %v\n", n, dst)  // 复制了 3 个元素: [1 2 3]// 切片内部复制(删除元素的常用技巧)
s := []int{1, 2, 3, 4, 5}
// 删除索引为 2 的元素
copy(s[2:], s[3:])
s = s[:len(s)-1]
fmt.Println(s)  // [1 2 4 5]

5. 切片的内存模型和共享

5.1 底层数组共享

// 多个切片可以共享同一个底层数组
original := []int{1, 2, 3, 4, 5}
slice1 := original[1:3]  // [2, 3]
slice2 := original[2:4]  // [3, 4]// 修改 slice1 会影响 original 和 slice2
slice1[1] = 99
fmt.Println("original:", original)  // [1, 2, 99, 4, 5]
fmt.Println("slice1:", slice1)      // [2, 99]
fmt.Println("slice2:", slice2)      // [99, 4]

5.2 扩容机制

func demonstrateGrowth() {s := make([]int, 0, 2)fmt.Printf("初始: len=%d, cap=%d, ptr=%p\n", len(s), cap(s), &s)for i := 1; i <= 5; i++ {s = append(s, i)fmt.Printf("添加%d: len=%d, cap=%d, ptr=%p\n", i, len(s), cap(s), &s)}
}
// 输出示例:
// 初始: len=0, cap=2, ptr=0xc000010018
// 添加1: len=1, cap=2, ptr=0xc000010018
// 添加2: len=2, cap=2, ptr=0xc000010018
// 添加3: len=3, cap=4, ptr=0xc000010018  // 扩容了,但切片结构地址不变
// 添加4: len=4, cap=4, ptr=0xc000010018
// 添加5: len=5, cap=8, ptr=0xc000010018  // 再次扩容

6. 切片作为函数参数

6.1 传递机制详解

// 切片传递的是结构体副本,但共享底层数组
func modifySlice(s []int) {// 修改元素:影响原切片(共享底层数组)if len(s) > 0 {s[0] = 999}// 修改切片结构:不影响原切片(只修改了副本)s = append(s, 100)fmt.Println("函数内:", s)
}func modifySlicePtr(s *[]int) {// 通过指针修改:影响原切片if len(*s) > 0 {(*s)[0] = 888}// 通过指针修改切片结构:影响原切片*s = append(*s, 200)fmt.Println("函数内:", *s)
}func main() {original := []int{1, 2, 3}fmt.Println("调用前:", original)modifySlice(original)fmt.Println("调用后:", original)  // [999, 2, 3] - 元素被修改,但没有新元素modifySlicePtr(&original)fmt.Println("指针调用后:", original)  // [888, 2, 3, 200] - 完全被修改
}

7. 切片的高级用法

7.1 二维切片

// 创建二维切片
matrix := make([][]int, 3)
for i := range matrix {matrix[i] = make([]int, 4)
}// 或者直接初始化
matrix2 := [][]int{{1, 2, 3},{4, 5, 6},{7, 8, 9},
}

7.2 切片排序

import "sort"// 基本类型排序
nums := []int{3, 1, 4, 1, 5, 9}
sort.Ints(nums)
fmt.Println(nums)  // [1, 1, 3, 4, 5, 9]// 自定义排序
type Person struct {Name stringAge  int
}people := []Person{{"Alice", 30},{"Bob", 25},{"Charlie", 35},
}// 按年龄排序
sort.Slice(people, func(i, j int) bool {return people[i].Age < people[j].Age
})

7.3 切片去重

func removeDuplicates(slice []int) []int {seen := make(map[int]bool)result := []int{}for _, item := range slice {if !seen[item] {seen[item] = trueresult = append(result, item)}}return result
}

8. 性能优化建议

8.1 预分配容量

// ❌ 低效:频繁扩容
var result []int
for i := 0; i < 1000; i++ {result = append(result, i)
}// ✅ 高效:预分配容量
result := make([]int, 0, 1000)
for i := 0; i < 1000; i++ {result = append(result, i)
}

8.2 避免内存泄漏

// ❌ 可能导致内存泄漏
func getFirstThree(large []int) []int {return large[:3]  // 仍然引用整个大数组
}// ✅ 正确做法
func getFirstThree(large []int) []int {result := make([]int, 3)copy(result, large[:3])return result
}

9. 常见陷阱和注意事项

9.1 循环中的切片修改

// ❌ 错误:在循环中修改切片长度
slice := []int{1, 2, 3, 4, 5}
for i, v := range slice {if v%2 == 0 {slice = append(slice[:i], slice[i+1:]...)  // 危险!}
}// ✅ 正确:从后往前删除或使用新切片
slice := []int{1, 2, 3, 4, 5}
for i := len(slice) - 1; i >= 0; i-- {if slice[i]%2 == 0 {slice = append(slice[:i], slice[i+1:]...)}
}

9.2 nil 切片 vs 空切片

var nilSlice []int              // nil 切片
emptySlice := []int{}           // 空切片
madeSlice := make([]int, 0)     // 空切片fmt.Println(nilSlice == nil)    // true
fmt.Println(emptySlice == nil)  // false
fmt.Println(madeSlice == nil)   // false// 但在使用上基本相同
fmt.Println(len(nilSlice))      // 0
fmt.Println(len(emptySlice))    // 0
http://www.dtcms.com/a/391781.html

相关文章:

  • 《Java网络编程》第二章:Stream流
  • 速卖通“AI全站推”上新!跨境卖家如何利用 AI 做营销
  • Apple Developer Program注册被拒原因分析与技术性申诉指南
  • 卷积神经网络从入门到经典模型详解
  • C#超市商品管理系统入门级实现
  • 自注意力机制Self-Attention (二)
  • InternVLA-N1——规划-执行双系统下的VLN基础模型:具备学习型的潜在规划能力,可部署在轮式、四足、双足人形上
  • 睡眠PSG数据集技术
  • 【AI落地应用实战】利用亚马逊云科技 Step Functions 集成现有系统快速实现个性化邮件触达
  • 手机备忘录待办APP工具评测
  • 总线错误(Bus Error)是什么?
  • 《Java网络编程》第三章:Internet地址
  • 【软件项目验收:第三方软件测评报告合规性和有效性审核,辨别真假软件测评报告书】
  • 变邻域含变惯性权重策略的自适应离散粒子群算法
  • cocos通过碰撞collider进行道具获取 或者出发事件
  • 自动化测试可行性分析
  • 三轴云台之抗干扰设计篇
  • Kubernetes 高级运维:监控升级、ETCD 备份与 Kustomize 配置管理
  • 计算机专业《软件工程》:构建数字世界的基石
  • 苹果组织/企业开发者账号(ADP)申请核心材料与技术审核要点
  • TLS 1.3加密加持:云环境Redis安全传输最佳实践
  • CS231n学习笔记3-3: DDPM
  • 抗辐照MCU在核电站巡检机器人摄像头模组中的应用探讨
  • 机器人编程教育闭环:校内外学习无缝衔接的设计思路
  • 如何在不修改域名解析的情况下检查WordPress网站迁移是否成功
  • JEL机器人使用经验分享(寻边器校准失败,晶圆偏移量太大,放入平台后发现每一片的位置都不一样)
  • 充电器自动化测试系统有哪些测试项目和方法?
  • 深度学习-卷积神经网络
  • ROS python程序将本地照片转为topic
  • 多态及其原理