/* 02-slices.go - Go 语言切片详解 学习目标: 1. 理解切片的概念和内部结构 2. 掌握切片的创建和初始化 3. 学会切片的各种操作 4. 了解切片的扩容机制 5. 掌握切片的实际应用场景 知识点: - 切片的定义和特性 - 切片的创建方式 - 切片的操作(append、copy、切片表达式) - 切片的内部结构(指针、长度、容量) - 切片的扩容机制 - 切片的内存管理 */ package main import "fmt" func main() { fmt.Println("=== Go 语言切片详解 ===\n") // 演示切片的基本概念 demonstrateBasicSlices() // 演示切片的创建方式 demonstrateSliceCreation() // 演示切片的基本操作 demonstrateSliceOperations() // 演示切片的内部结构 demonstrateSliceInternals() // 演示切片的扩容机制 demonstrateSliceGrowth() // 演示切片的高级操作 demonstrateAdvancedSliceOperations() // 演示切片的实际应用 demonstratePracticalApplications() } // demonstrateBasicSlices 演示切片的基本概念 func demonstrateBasicSlices() { fmt.Println("1. 切片的基本概念:") // 切片的基本特性 fmt.Printf(" 切片的基本特性:\n") fmt.Printf(" - 动态数组,长度可变\n") fmt.Printf(" - 引用类型,指向底层数组\n") fmt.Printf(" - 有长度(len)和容量(cap)两个属性\n") fmt.Printf(" - 零值是 nil\n") fmt.Printf(" - 不能直接比较,只能与 nil 比较\n") // 基本切片示例 fmt.Printf(" 基本切片示例:\n") var slice []int // 声明切片,零值为 nil fmt.Printf(" 声明切片: %v\n", slice) fmt.Printf(" 切片长度: %d\n", len(slice)) fmt.Printf(" 切片容量: %d\n", cap(slice)) fmt.Printf(" 是否为 nil: %t\n", slice == nil) // 初始化切片 numbers := []int{1, 2, 3, 4, 5} fmt.Printf(" 初始化切片: %v\n", numbers) fmt.Printf(" 长度: %d, 容量: %d\n", len(numbers), cap(numbers)) // 切片元素访问 fmt.Printf(" 第一个元素: %d\n", numbers[0]) fmt.Printf(" 最后一个元素: %d\n", numbers[len(numbers)-1]) fmt.Println() } // demonstrateSliceCreation 演示切片的创建方式 func demonstrateSliceCreation() { fmt.Println("2. 切片的创建方式:") // 方式1: 字面量创建 fmt.Printf(" 方式1 - 字面量创建:\n") slice1 := []string{"Go", "Python", "Java"} fmt.Printf(" slice1: %v (len=%d, cap=%d)\n", slice1, len(slice1), cap(slice1)) // 方式2: make 函数创建 fmt.Printf(" 方式2 - make 函数创建:\n") slice2 := make([]int, 5) // 长度为5,容量为5 slice3 := make([]int, 3, 10) // 长度为3,容量为10 fmt.Printf(" slice2: %v (len=%d, cap=%d)\n", slice2, len(slice2), cap(slice2)) fmt.Printf(" slice3: %v (len=%d, cap=%d)\n", slice3, len(slice3), cap(slice3)) // 方式3: 从数组创建 fmt.Printf(" 方式3 - 从数组创建:\n") arr := [6]int{1, 2, 3, 4, 5, 6} slice4 := arr[:] // 全部元素 slice5 := arr[1:4] // 索引1到3 slice6 := arr[:3] // 开头到索引2 slice7 := arr[2:] // 索引2到结尾 fmt.Printf(" 原数组: %v\n", arr) fmt.Printf(" arr[:]: %v (len=%d, cap=%d)\n", slice4, len(slice4), cap(slice4)) fmt.Printf(" arr[1:4]: %v (len=%d, cap=%d)\n", slice5, len(slice5), cap(slice5)) fmt.Printf(" arr[:3]: %v (len=%d, cap=%d)\n", slice6, len(slice6), cap(slice6)) fmt.Printf(" arr[2:]: %v (len=%d, cap=%d)\n", slice7, len(slice7), cap(slice7)) // 方式4: 从切片创建 fmt.Printf(" 方式4 - 从切片创建:\n") original := []int{10, 20, 30, 40, 50} slice8 := original[1:4] slice9 := slice8[1:3] fmt.Printf(" 原切片: %v\n", original) fmt.Printf(" original[1:4]: %v (len=%d, cap=%d)\n", slice8, len(slice8), cap(slice8)) fmt.Printf(" slice8[1:3]: %v (len=%d, cap=%d)\n", slice9, len(slice9), cap(slice9)) // 方式5: 三索引切片表达式 fmt.Printf(" 方式5 - 三索引切片表达式:\n") data := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} slice10 := data[2:5:7] // [low:high:max] 限制容量 fmt.Printf(" data[2:5:7]: %v (len=%d, cap=%d)\n", slice10, len(slice10), cap(slice10)) fmt.Println() }// de monstrateSliceOperations 演示切片的基本操作 func demonstrateSliceOperations() { fmt.Println("3. 切片的基本操作:") // append 操作 fmt.Printf(" append 操作:\n") var fruits []string fmt.Printf(" 初始切片: %v (len=%d, cap=%d)\n", fruits, len(fruits), cap(fruits)) // 添加单个元素 fruits = append(fruits, "apple") fmt.Printf(" 添加 apple: %v (len=%d, cap=%d)\n", fruits, len(fruits), cap(fruits)) // 添加多个元素 fruits = append(fruits, "banana", "cherry") fmt.Printf(" 添加多个: %v (len=%d, cap=%d)\n", fruits, len(fruits), cap(fruits)) // 添加另一个切片 moreFruits := []string{"date", "elderberry"} fruits = append(fruits, moreFruits...) fmt.Printf(" 添加切片: %v (len=%d, cap=%d)\n", fruits, len(fruits), cap(fruits)) // copy 操作 fmt.Printf(" copy 操作:\n") source := []int{1, 2, 3, 4, 5} dest := make([]int, 3) n := copy(dest, source) fmt.Printf(" 源切片: %v\n", source) fmt.Printf(" 目标切片: %v\n", dest) fmt.Printf(" 复制了 %d 个元素\n", n) // copy 的特殊用法 numbers := []int{1, 2, 3, 4, 5, 6} copy(numbers[2:], numbers[1:]) // 向右移动元素 fmt.Printf(" 移动后: %v\n", numbers) // 切片遍历 fmt.Printf(" 切片遍历:\n") colors := []string{"red", "green", "blue", "yellow"} // 使用索引遍历 fmt.Printf(" 使用索引遍历:\n") for i := 0; i < len(colors); i++ { fmt.Printf(" colors[%d] = %s\n", i, colors[i]) } // 使用 range 遍历 fmt.Printf(" 使用 range 遍历:\n") for index, color := range colors { fmt.Printf(" colors[%d] = %s\n", index, color) } // 切片修改 fmt.Printf(" 切片修改:\n") nums := []int{10, 20, 30, 40, 50} fmt.Printf(" 原切片: %v\n", nums) nums[2] = 99 fmt.Printf(" 修改索引2: %v\n", nums) // 批量修改 for i := range nums { nums[i] *= 2 } fmt.Printf(" 全部乘以2: %v\n", nums) fmt.Println() } // demonstrateSliceInternals 演示切片的内部结构 func demonstrateSliceInternals() { fmt.Println("4. 切片的内部结构:") fmt.Printf(" 切片内部结构包含三个字段:\n") fmt.Printf(" - 指针:指向底层数组的指针\n") fmt.Printf(" - 长度:切片中元素的个数\n") fmt.Printf(" - 容量:从切片起始位置到底层数组末尾的元素个数\n") // 演示切片和底层数组的关系 fmt.Printf(" 切片和底层数组的关系:\n") arr := [8]int{0, 1, 2, 3, 4, 5, 6, 7} slice := arr[2:6] fmt.Printf(" 原数组: %v\n", arr) fmt.Printf(" 切片 arr[2:6]: %v (len=%d, cap=%d)\n", slice, len(slice), cap(slice)) // 修改切片影响底层数组 slice[1] = 99 fmt.Printf(" 修改切片后:\n") fmt.Printf(" 数组: %v\n", arr) fmt.Printf(" 切片: %v\n", slice) // 多个切片共享底层数组 fmt.Printf(" 多个切片共享底层数组:\n") slice1 := arr[1:4] slice2 := arr[3:7] fmt.Printf(" slice1 (arr[1:4]): %v\n", slice1) fmt.Printf(" slice2 (arr[3:7]): %v\n", slice2) slice1[2] = 888 // 修改 arr[3] fmt.Printf(" 修改 slice1[2] 后:\n") fmt.Printf(" slice1: %v\n", slice1) fmt.Printf(" slice2: %v (第一个元素也变了)\n", slice2) fmt.Printf(" 数组: %v\n", arr) // 切片的容量计算 fmt.Printf(" 切片的容量计算:\n") data := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} s1 := data[2:5] // 从索引2开始,容量是到数组末尾 s2 := data[2:5:7] // 限制容量到索引7 fmt.Printf(" data[2:5]: %v (len=%d, cap=%d)\n", s1, len(s1), cap(s1)) fmt.Printf(" data[2:5:7]: %v (len=%d, cap=%d)\n", s2, len(s2), cap(s2)) fmt.Println() } // demonstrateSliceGrowth 演示切片的扩容机制 func demonstrateSliceGrowth() { fmt.Println("5. 切片的扩容机制:") fmt.Printf(" 切片扩容规则:\n") fmt.Printf(" - 当 append 时容量不足,会创建新的底层数组\n") fmt.Printf(" - 新容量通常是原容量的2倍(小切片)\n") fmt.Printf(" - 大切片的扩容策略更复杂\n") // 演示扩容过程 fmt.Printf(" 扩容过程演示:\n") var slice []int for i := 0; i < 10; i++ { oldCap := cap(slice) slice = append(slice, i) newCap := cap(slice) if newCap != oldCap { fmt.Printf(" 扩容: 长度 %d, 容量 %d -> %d\n", len(slice), oldCap, newCap) } } fmt.Printf(" 最终切片: %v (len=%d, cap=%d)\n", slice, len(slice), cap(slice)) // 预分配容量避免频繁扩容 fmt.Printf(" 预分配容量优化:\n") // 不好的做法:频繁扩容 var badSlice []int for i := 0; i < 1000; i++ { badSlice = append(badSlice, i) } fmt.Printf(" 频繁扩容结果: len=%d, cap=%d\n", len(badSlice), cap(badSlice)) // 好的做法:预分配容量 goodSlice := make([]int, 0, 1000) // 预分配容量1000 for i := 0; i < 1000; i++ { goodSlice = append(goodSlice, i) } fmt.Printf(" 预分配结果: len=%d, cap=%d\n", len(goodSlice), cap(goodSlice)) // 扩容时的内存重新分配 fmt.Printf(" 扩容时的内存重新分配:\n") original := []int{1, 2, 3} fmt.Printf(" 原切片: %v (cap=%d)\n", original, cap(original)) // 创建基于原切片的新切片 derived := original[1:2] fmt.Printf(" 派生切片: %v (cap=%d)\n", derived, cap(derived)) // 扩容派生切片 derived = append(derived, 99, 100, 101, 102) fmt.Printf(" 扩容后派生切片: %v (cap=%d)\n", derived, cap(derived)) fmt.Printf(" 原切片: %v (未受影响)\n", original) fmt.Println() } // demonstrateAdvancedSliceOperations 演示切片的高级操作 func demonstrateAdvancedSliceOperations() { fmt.Println("6. 切片的高级操作:") // 切片删除元素 fmt.Printf(" 切片删除元素:\n") numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} fmt.Printf(" 原切片: %v\n", numbers) // 删除索引为3的元素 index := 3 numbers = append(numbers[:index], numbers[index+1:]...) fmt.Printf(" 删除索引3: %v\n", numbers) // 删除多个元素(索引1到3) start, end := 1, 4 numbers = append(numbers[:start], numbers[end:]...) fmt.Printf(" 删除索引1-3: %v\n", numbers) // 切片插入元素 fmt.Printf(" 切片插入元素:\n") data := []int{1, 2, 5, 6} fmt.Printf(" 原切片: %v\n", data) // 在索引2插入元素3和4 insertIndex := 2 insertValues := []int{3, 4} // 方法1:使用 append 和切片操作 data = append(data[:insertIndex], append(insertValues, data[insertIndex:]...)...) fmt.Printf(" 插入3,4: %v\n", data) // 切片反转 fmt.Printf(" 切片反转:\n") original := []string{"a", "b", "c", "d", "e"} reversed := make([]string, len(original)) for i, v := range original { reversed[len(original)-1-i] = v } fmt.Printf(" 原切片: %v\n", original) fmt.Printf(" 反转后: %v\n", reversed) // 就地反转 toReverse := []int{1, 2, 3, 4, 5} fmt.Printf(" 就地反转前: %v\n", toReverse) for i, j := 0, len(toReverse)-1; i < j; i, j = i+1, j-1 { toReverse[i], toReverse[j] = toReverse[j], toReverse[i] } fmt.Printf(" 就地反转后: %v\n", toReverse) // 切片去重 fmt.Printf(" 切片去重:\n") withDuplicates := []int{1, 2, 2, 3, 3, 3, 4, 5, 5} unique := removeDuplicates(withDuplicates) fmt.Printf(" 有重复: %v\n", withDuplicates) fmt.Printf(" 去重后: %v\n", unique) // 切片过滤 fmt.Printf(" 切片过滤:\n") allNumbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} evenNumbers := filter(allNumbers, func(n int) bool { return n%2 == 0 }) fmt.Printf(" 所有数字: %v\n", allNumbers) fmt.Printf(" 偶数: %v\n", evenNumbers) fmt.Println() }/ / demonstratePracticalApplications 演示切片的实际应用 func demonstratePracticalApplications() { fmt.Println("7. 切片的实际应用:") // 应用1: 动态数组 fmt.Printf(" 应用1 - 动态数组(购物车):\n") var cart []string // 添加商品 cart = append(cart, "笔记本电脑", "鼠标", "键盘") fmt.Printf(" 购物车: %v\n", cart) // 移除商品(移除鼠标) for i, item := range cart { if item == "鼠标" { cart = append(cart[:i], cart[i+1:]...) break } } fmt.Printf(" 移除鼠标后: %v\n", cart) // 应用2: 缓冲区和队列 fmt.Printf(" 应用2 - 简单队列实现:\n") queue := NewQueue() // 入队 queue.Enqueue("任务1") queue.Enqueue("任务2") queue.Enqueue("任务3") fmt.Printf(" 队列状态: %v\n", queue.items) // 出队 item := queue.Dequeue() fmt.Printf(" 出队: %s\n", item) fmt.Printf(" 队列状态: %v\n", queue.items) // 应用3: 栈实现 fmt.Printf(" 应用3 - 简单栈实现:\n") stack := NewStack() // 入栈 stack.Push(10) stack.Push(20) stack.Push(30) fmt.Printf(" 栈状态: %v\n", stack.items) // 出栈 value := stack.Pop() fmt.Printf(" 出栈: %d\n", value) fmt.Printf(" 栈状态: %v\n", stack.items) // 应用4: 数据处理管道 fmt.Printf(" 应用4 - 数据处理管道:\n") rawData := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} // 管道处理:过滤偶数 -> 平方 -> 求和 evenNumbers := filter(rawData, func(n int) bool { return n%2 == 0 }) squares := mapInts(evenNumbers, func(n int) int { return n * n }) sum := reduce(squares, 0, func(acc, n int) int { return acc + n }) fmt.Printf(" 原始数据: %v\n", rawData) fmt.Printf(" 偶数: %v\n", evenNumbers) fmt.Printf(" 平方: %v\n", squares) fmt.Printf(" 求和: %d\n", sum) // 应用5: 分页处理 fmt.Printf(" 应用5 - 分页处理:\n") allItems := []string{ "item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9", "item10", "item11", "item12", "item13", "item14", "item15", } pageSize := 5 totalPages := (len(allItems) + pageSize - 1) / pageSize for page := 0; page < totalPages; page++ { start := page * pageSize end := start + pageSize if end > len(allItems) { end = len(allItems) } pageItems := allItems[start:end] fmt.Printf(" 第%d页: %v\n", page+1, pageItems) } // 应用6: 批处理 fmt.Printf(" 应用6 - 批处理:\n") tasks := []string{ "task1", "task2", "task3", "task4", "task5", "task6", "task7", "task8", "task9", "task10", } batchSize := 3 for i := 0; i < len(tasks); i += batchSize { end := i + batchSize if end > len(tasks) { end = len(tasks) } batch := tasks[i:end] fmt.Printf(" 处理批次: %v\n", batch) // 这里可以并发处理这个批次 } // 应用7: 滑动窗口 fmt.Printf(" 应用7 - 滑动窗口(移动平均):\n") prices := []float64{10.5, 11.2, 10.8, 12.1, 11.9, 12.5, 11.8, 12.3, 13.0, 12.7} windowSize := 3 fmt.Printf(" 价格序列: %v\n", prices) fmt.Printf(" %d日移动平均:\n", windowSize) for i := 0; i <= len(prices)-windowSize; i++ { window := prices[i : i+windowSize] average := 0.0 for _, price := range window { average += price } average /= float64(len(window)) fmt.Printf(" 第%d-%d日: %.2f\n", i+1, i+windowSize, average) } fmt.Println() } // ========== 辅助函数和类型 ========== // 去重函数 func removeDuplicates(slice []int) []int { seen := make(map[int]bool) var result []int for _, v := range slice { if !seen[v] { seen[v] = true result = append(result, v) } } return result } // 过滤函数 func filter(slice []int, predicate func(int) bool) []int { var result []int for _, v := range slice { if predicate(v) { result = append(result, v) } } return result } // 映射函数 func mapInts(slice []int, mapper func(int) int) []int { result := make([]int, len(slice)) for i, v := range slice { result[i] = mapper(v) } return result } // 归约函数 func reduce(slice []int, initial int, reducer func(int, int) int) int { result := initial for _, v := range slice { result = reducer(result, v) } return result } // 简单队列实现 type Queue struct { items []string } func NewQueue() *Queue { return &Queue{items: make([]string, 0)} } func (q *Queue) Enqueue(item string) { q.items = append(q.items, item) } func (q *Queue) Dequeue() string { if len(q.items) == 0 { return "" } item := q.items[0] q.items = q.items[1:] return item } func (q *Queue) IsEmpty() bool { return len(q.items) == 0 } // 简单栈实现 type Stack struct { items []int } func NewStack() *Stack { return &Stack{items: make([]int, 0)} } func (s *Stack) Push(item int) { s.items = append(s.items, item) } func (s *Stack) Pop() int { if len(s.items) == 0 { return 0 } index := len(s.items) - 1 item := s.items[index] s.items = s.items[:index] return item } func (s *Stack) IsEmpty() bool { return len(s.items) == 0 } func (s *Stack) Peek() int { if len(s.items) == 0 { return 0 } return s.items[len(s.items)-1] } /* 运行这个程序: go run 02-slices.go 学习要点: 1. 切片是动态数组,长度可变,是引用类型 2. 切片有长度(len)和容量(cap)两个属性 3. 切片的零值是 nil,不能直接比较 4. append 操作可能触发扩容,创建新的底层数组 5. 多个切片可以共享同一个底层数组 切片的内部结构: 1. 指针:指向底层数组的指针 2. 长度:切片中元素的个数 3. 容量:从切片起始位置到底层数组末尾的元素个数 创建切片的方式: 1. 字面量:[]Type{values...} 2. make函数:make([]Type, len, cap) 3. 从数组/切片:array[start:end] 4. 三索引表达式:slice[start:end:cap] 切片操作: 1. append:添加元素,可能扩容 2. copy:复制元素 3. 切片表达式:获取子切片 4. 遍历:for range 或索引循环 扩容机制: 1. 容量不足时创建新数组 2. 小切片通常扩容为原来的2倍 3. 大切片扩容策略更复杂 4. 预分配容量可以避免频繁扩容 最佳实践: 1. 预分配容量避免频繁扩容 2. 使用三索引表达式限制容量 3. 注意切片共享底层数组的问题 4. 大切片传参考虑性能影响 5. 及时释放不需要的切片引用 常见应用场景: 1. 动态数组和列表 2. 栈和队列实现 3. 缓冲区和管道 4. 数据处理和过滤 5. 分页和批处理 6. 滑动窗口算法 注意事项: 1. 切片是引用类型,修改会影响底层数组 2. 切片扩容会创建新数组,断开与原数组的联系 3. 空切片和nil切片的区别 4. 切片越界会panic 5. 大切片的内存泄漏问题 */