diff --git a/.kiro/specs/golang-learning-guide/tasks.md b/.kiro/specs/golang-learning-guide/tasks.md index a3c6e40..7881efc 100644 --- a/.kiro/specs/golang-learning-guide/tasks.md +++ b/.kiro/specs/golang-learning-guide/tasks.md @@ -46,13 +46,13 @@ - _需求: 3.1, 3.2, 3.3_ - [ ] 5. 实现数据结构学习模块 -- [-] 5.1 创建基本数据结构示例 +- [x] 5.1 创建基本数据结构示例 - 编写 04-data-structures/01-arrays.go,展示数组的创建和操作 - 编写 04-data-structures/02-slices.go,演示切片的各种操作方法 - 编写 04-data-structures/03-maps.go,展示映射的增删改查操作 - _需求: 4.1, 4.2, 4.3_ -- [ ] 5.2 创建复合数据结构示例 +- [x] 5.2 创建复合数据结构示例 - 编写 04-data-structures/04-structs.go,展示结构体的定义和使用 - 编写 04-data-structures/05-pointers.go,演示指针的概念和操作 - 提供不同数据结构的使用场景建议 diff --git a/golang-learning/04-data-structures/01-arrays.go b/golang-learning/04-data-structures/01-arrays.go index d3c8881..1d1f17b 100644 --- a/golang-learning/04-data-structures/01-arrays.go +++ b/golang-learning/04-data-structures/01-arrays.go @@ -196,4 +196,359 @@ func demonstrateArrayOperations() { } fmt.Println() -} \ No newline at end of file +}// de +monstrateMultiDimensionalArrays 演示多维数组 +func demonstrateMultiDimensionalArrays() { + fmt.Println("4. 多维数组:") + + // 二维数组 + fmt.Printf(" 二维数组:\n") + + // 声明和初始化二维数组 + var matrix [3][4]int + fmt.Printf(" 声明 3x4 矩阵: %v\n", matrix) + + // 初始化二维数组 + matrix2 := [2][3]int{ + {1, 2, 3}, + {4, 5, 6}, + } + fmt.Printf(" 初始化 2x3 矩阵:\n") + for i, row := range matrix2 { + fmt.Printf(" 行 %d: %v\n", i, row) + } + + // 访问和修改二维数组元素 + fmt.Printf(" 访问元素 matrix2[1][2]: %d\n", matrix2[1][2]) + matrix2[0][1] = 99 + fmt.Printf(" 修改后 matrix2[0][1]: %d\n", matrix2[0][1]) + + // 遍历二维数组 + fmt.Printf(" 遍历二维数组:\n") + for i := 0; i < len(matrix2); i++ { + for j := 0; j < len(matrix2[i]); j++ { + fmt.Printf(" matrix2[%d][%d] = %d\n", i, j, matrix2[i][j]) + } + } + + // 使用 range 遍历二维数组 + fmt.Printf(" 使用 range 遍历:\n") + for i, row := range matrix2 { + for j, value := range row { + fmt.Printf(" [%d][%d] = %d\n", i, j, value) + } + } + + // 三维数组 + fmt.Printf(" 三维数组:\n") + cube := [2][2][2]int{ + { + {1, 2}, + {3, 4}, + }, + { + {5, 6}, + {7, 8}, + }, + } + + fmt.Printf(" 3D 数组:\n") + for i, plane := range cube { + fmt.Printf(" 平面 %d:\n", i) + for j, row := range plane { + fmt.Printf(" 行 %d: %v\n", j, row) + } + } + + // 实际应用:游戏棋盘 + fmt.Printf(" 实际应用 - 井字棋棋盘:\n") + board := [3][3]string{ + {"X", "O", "X"}, + {"O", "X", "O"}, + {"X", "O", "X"}, + } + + fmt.Printf(" 井字棋棋盘:\n") + for i, row := range board { + fmt.Printf(" ") + for j, cell := range row { + fmt.Printf("%s", cell) + if j < len(row)-1 { + fmt.Printf(" | ") + } + } + fmt.Printf("\n") + if i < len(board)-1 { + fmt.Printf(" ---------\n") + } + } + + fmt.Println() +} + +// demonstrateArrayProperties 演示数组的特性 +func demonstrateArrayProperties() { + fmt.Println("5. 数组的特性:") + + // 数组是值类型 + fmt.Printf(" 数组是值类型:\n") + arr1 := [3]int{1, 2, 3} + arr2 := arr1 // 拷贝整个数组 + + fmt.Printf(" 原数组 arr1: %v\n", arr1) + fmt.Printf(" 拷贝数组 arr2: %v\n", arr2) + + // 修改拷贝不影响原数组 + arr2[0] = 99 + fmt.Printf(" 修改 arr2[0] 后:\n") + fmt.Printf(" arr1: %v (未改变)\n", arr1) + fmt.Printf(" arr2: %v (已改变)\n", arr2) + + // 数组比较 + fmt.Printf(" 数组比较:\n") + a := [3]int{1, 2, 3} + b := [3]int{1, 2, 3} + c := [3]int{1, 2, 4} + + fmt.Printf(" a == b: %t\n", a == b) + fmt.Printf(" a == c: %t\n", a == c) + fmt.Printf(" a != c: %t\n", a != c) + + // 注意:不同长度的数组是不同类型,不能比较 + // var d [4]int + // fmt.Println(a == d) // 编译错误 + + // 数组作为函数参数 + fmt.Printf(" 数组作为函数参数:\n") + original := [3]int{10, 20, 30} + fmt.Printf(" 调用前: %v\n", original) + + modifyArrayByValue(original) + fmt.Printf(" 值传递后: %v (未改变)\n", original) + + modifyArrayByPointer(&original) + fmt.Printf(" 指针传递后: %v (已改变)\n", original) + + // 数组的零值 + fmt.Printf(" 数组的零值:\n") + var zeroIntArray [3]int + var zeroStringArray [2]string + var zeroBoolArray [2]bool + + fmt.Printf(" int 数组零值: %v\n", zeroIntArray) + fmt.Printf(" string 数组零值: %v\n", zeroStringArray) + fmt.Printf(" bool 数组零值: %v\n", zeroBoolArray) + + // 数组长度是编译时常量 + fmt.Printf(" 数组长度是编译时常量:\n") + const size = 5 + constArray := [size]int{1, 2, 3, 4, 5} + fmt.Printf(" 使用常量定义数组: %v\n", constArray) + + fmt.Println() +} + +// demonstratePracticalApplications 演示数组的实际应用 +func demonstratePracticalApplications() { + fmt.Println("6. 数组的实际应用:") + + // 应用1: 统计和分析 + fmt.Printf(" 应用1 - 成绩统计:\n") + scores := [10]int{85, 92, 78, 96, 88, 73, 91, 87, 94, 82} + + // 计算总分和平均分 + total := 0 + for _, score := range scores { + total += score + } + average := float64(total) / float64(len(scores)) + + // 找最高分和最低分 + max, min := scores[0], scores[0] + for _, score := range scores { + if score > max { + max = score + } + if score < min { + min = score + } + } + + fmt.Printf(" 成绩: %v\n", scores) + fmt.Printf(" 总分: %d\n", total) + fmt.Printf(" 平均分: %.2f\n", average) + fmt.Printf(" 最高分: %d\n", max) + fmt.Printf(" 最低分: %d\n", min) + + // 应用2: 频率统计 + fmt.Printf(" 应用2 - 字符频率统计:\n") + text := "hello world" + var frequency [26]int // 26个字母的频率 + + for _, char := range text { + if char >= 'a' && char <= 'z' { + frequency[char-'a']++ + } + } + + fmt.Printf(" 文本: \"%s\"\n", text) + fmt.Printf(" 字母频率:\n") + for i, count := range frequency { + if count > 0 { + fmt.Printf(" %c: %d\n", 'a'+i, count) + } + } + + // 应用3: 查找表 + fmt.Printf(" 应用3 - 月份天数查找表:\n") + daysInMonth := [12]int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} + months := [12]string{ + "一月", "二月", "三月", "四月", "五月", "六月", + "七月", "八月", "九月", "十月", "十一月", "十二月", + } + + for i := 0; i < len(months); i++ { + fmt.Printf(" %s: %d天\n", months[i], daysInMonth[i]) + } + + // 应用4: 缓冲区 + fmt.Printf(" 应用4 - 固定大小缓冲区:\n") + buffer := [8]byte{} + data := []byte("Hello") + + // 将数据复制到缓冲区 + copy(buffer[:], data) + fmt.Printf(" 缓冲区: %v\n", buffer) + fmt.Printf(" 缓冲区内容: %s\n", string(buffer[:len(data)])) + + // 应用5: 矩阵运算 + fmt.Printf(" 应用5 - 矩阵加法:\n") + matrixA := [2][2]int{{1, 2}, {3, 4}} + matrixB := [2][2]int{{5, 6}, {7, 8}} + var result [2][2]int + + // 矩阵加法 + for i := 0; i < 2; i++ { + for j := 0; j < 2; j++ { + result[i][j] = matrixA[i][j] + matrixB[i][j] + } + } + + fmt.Printf(" 矩阵A: %v\n", matrixA) + fmt.Printf(" 矩阵B: %v\n", matrixB) + fmt.Printf(" A + B: %v\n", result) + + fmt.Println() +} + +// demonstrateArrayVsSlice 演示数组 vs 切片 +func demonstrateArrayVsSlice() { + fmt.Println("7. 数组 vs 切片:") + + fmt.Printf(" 数组特点:\n") + fmt.Printf(" - 固定长度,长度是类型的一部分\n") + fmt.Printf(" - 值类型,赋值和传参会拷贝\n") + fmt.Printf(" - 内存效率高,栈上分配\n") + fmt.Printf(" - 编译时确定大小\n") + fmt.Printf(" - 可以直接比较\n") + + fmt.Printf(" 切片特点:\n") + fmt.Printf(" - 动态长度,可以增长和缩减\n") + fmt.Printf(" - 引用类型,赋值和传参传递引用\n") + fmt.Printf(" - 更灵活,但有额外开销\n") + fmt.Printf(" - 运行时动态分配\n") + fmt.Printf(" - 不能直接比较\n") + + // 性能比较示例 + fmt.Printf(" 使用场景建议:\n") + fmt.Printf(" 数组适用于:\n") + fmt.Printf(" - 固定大小的数据集合\n") + fmt.Printf(" - 性能敏感的场景\n") + fmt.Printf(" - 需要值语义的场景\n") + fmt.Printf(" - 小规模数据\n") + + fmt.Printf(" 切片适用于:\n") + fmt.Printf(" - 动态大小的数据集合\n") + fmt.Printf(" - 需要灵活操作的场景\n") + fmt.Printf(" - 大规模数据处理\n") + fmt.Printf(" - 函数参数传递\n") + + // 转换示例 + fmt.Printf(" 数组和切片的转换:\n") + arr := [5]int{1, 2, 3, 4, 5} + slice := arr[:] // 数组转切片 + + fmt.Printf(" 原数组: %v\n", arr) + fmt.Printf(" 转换的切片: %v\n", slice) + + // 修改切片会影响原数组 + slice[0] = 99 + fmt.Printf(" 修改切片后:\n") + fmt.Printf(" 数组: %v\n", arr) + fmt.Printf(" 切片: %v\n", slice) + + fmt.Println() +} + +// ========== 辅助函数 ========== + +// 值传递数组(会拷贝整个数组) +func modifyArrayByValue(arr [3]int) { + arr[0] = 999 + fmt.Printf(" 函数内修改: %v\n", arr) +} + +// 指针传递数组(传递数组地址) +func modifyArrayByPointer(arr *[3]int) { + arr[0] = 888 + fmt.Printf(" 函数内修改: %v\n", *arr) +} + +/* +运行这个程序: +go run 01-arrays.go + +学习要点: +1. 数组是固定长度的元素序列,长度是类型的一部分 +2. 数组是值类型,赋值和传参会拷贝整个数组 +3. 数组的零值是所有元素都为零值的数组 +4. 数组可以直接比较(相同类型和长度) +5. 多维数组本质上是数组的数组 + +数组的特性: +1. 固定长度:编译时确定,不能改变 +2. 类型安全:所有元素必须是相同类型 +3. 连续内存:元素在内存中连续存储 +4. 索引访问:O(1) 时间复杂度 +5. 值语义:赋值和传参会拷贝 + +声明和初始化方式: +1. var arr [n]Type +2. arr := [n]Type{values...} +3. arr := [...]Type{values...} // 自动推断长度 +4. arr := [n]Type{index: value} // 指定索引 + +数组 vs 切片: +1. 数组:固定长度,值类型,性能高 +2. 切片:动态长度,引用类型,更灵活 + +使用建议: +1. 小规模、固定大小的数据使用数组 +2. 需要动态调整大小时使用切片 +3. 性能敏感的场景考虑数组 +4. 函数参数通常使用切片而不是数组 + +常见应用场景: +1. 固定大小的缓冲区 +2. 查找表和映射表 +3. 矩阵和多维数据 +4. 统计和频率分析 +5. 游戏棋盘等固定结构 + +注意事项: +1. 数组长度是类型的一部分 +2. 不同长度的数组是不同类型 +3. 数组传参会拷贝,大数组考虑传指针 +4. 数组索引越界会 panic +5. 多维数组的内存布局是行优先的 +*/ \ No newline at end of file diff --git a/golang-learning/04-data-structures/02-slices.go b/golang-learning/04-data-structures/02-slices.go new file mode 100644 index 0000000..6ecd523 --- /dev/null +++ b/golang-learning/04-data-structures/02-slices.go @@ -0,0 +1,672 @@ +/* +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. 大切片的内存泄漏问题 +*/ \ No newline at end of file diff --git a/golang-learning/04-data-structures/03-maps.go b/golang-learning/04-data-structures/03-maps.go new file mode 100644 index 0000000..8a4d18a --- /dev/null +++ b/golang-learning/04-data-structures/03-maps.go @@ -0,0 +1,722 @@ +/* +03-maps.go - Go 语言映射详解 + +学习目标: +1. 理解映射的概念和特性 +2. 掌握映射的创建和初始化 +3. 学会映射的基本操作 +4. 了解映射的内部实现 +5. 掌握映射的实际应用场景 + +知识点: +- 映射的定义和特性 +- 映射的创建方式 +- 映射的增删改查操作 +- 映射的遍历 +- 映射的零值和比较 +- 映射作为集合使用 +- 映射的并发安全问题 +*/ + +package main + +import ( + "fmt" +) + +func main() { + fmt.Println("=== Go 语言映射详解 ===\n") + + // 演示映射的基本概念 + demonstrateBasicMaps() + + // 演示映射的创建方式 + demonstrateMapCreation() + + // 演示映射的基本操作 + demonstrateMapOperations() + + // 演示映射的遍历 + demonstrateMapIteration() + + // 演示映射的高级操作 + demonstrateAdvancedMapOperations() + + // 演示映射作为集合使用 + demonstrateMapAsSet() + + // 演示映射的实际应用 + demonstratePracticalApplications() +} + +// demonstrateBasicMaps 演示映射的基本概念 +func demonstrateBasicMaps() { + fmt.Println("1. 映射的基本概念:") + + // 映射的基本特性 + fmt.Printf(" 映射的基本特性:\n") + fmt.Printf(" - 键值对的无序集合\n") + fmt.Printf(" - 键必须是可比较的类型\n") + fmt.Printf(" - 引用类型,零值是 nil\n") + fmt.Printf(" - 动态大小,可以增长和缩减\n") + fmt.Printf(" - 不能直接比较,只能与 nil 比较\n") + + // 基本映射示例 + fmt.Printf(" 基本映射示例:\n") + + var m map[string]int // 声明映射,零值为 nil + fmt.Printf(" 声明映射: %v\n", m) + fmt.Printf(" 是否为 nil: %t\n", m == nil) + fmt.Printf(" 长度: %d\n", len(m)) + + // 初始化映射 + scores := map[string]int{ + "Alice": 95, + "Bob": 87, + "Charlie": 92, + } + fmt.Printf(" 初始化映射: %v\n", scores) + fmt.Printf(" 长度: %d\n", len(scores)) + + // 访问映射元素 + fmt.Printf(" Alice的分数: %d\n", scores["Alice"]) + fmt.Printf(" 不存在的键: %d\n", scores["David"]) // 返回零值 + + fmt.Println() +} + +// demonstrateMapCreation 演示映射的创建方式 +func demonstrateMapCreation() { + fmt.Println("2. 映射的创建方式:") + + // 方式1: 字面量创建 + fmt.Printf(" 方式1 - 字面量创建:\n") + colors := map[string]string{ + "red": "#FF0000", + "green": "#00FF00", + "blue": "#0000FF", + } + fmt.Printf(" colors: %v\n", colors) + + // 方式2: make 函数创建 + fmt.Printf(" 方式2 - make 函数创建:\n") + ages := make(map[string]int) + ages["Alice"] = 25 + ages["Bob"] = 30 + fmt.Printf(" ages: %v\n", ages) + + // 方式3: 空映射字面量 + fmt.Printf(" 方式3 - 空映射字面量:\n") + empty := map[string]int{} + fmt.Printf(" empty: %v (len=%d)\n", empty, len(empty)) + fmt.Printf(" 是否为 nil: %t\n", empty == nil) + + // 方式4: 带容量提示的 make + fmt.Printf(" 方式4 - 带容量提示的 make:\n") + large := make(map[string]int, 100) // 容量提示,不是限制 + fmt.Printf(" large: %v (len=%d)\n", large, len(large)) + + // 不同类型的映射 + fmt.Printf(" 不同类型的映射:\n") + + // 整数键 + intMap := map[int]string{1: "one", 2: "two", 3: "three"} + fmt.Printf(" 整数键: %v\n", intMap) + + // 结构体值 + type Person struct { + Name string + Age int + } + + people := map[string]Person{ + "p1": {"Alice", 25}, + "p2": {"Bob", 30}, + } + fmt.Printf(" 结构体值: %v\n", people) + + // 切片值 + groups := map[string][]string{ + "fruits": {"apple", "banana", "cherry"}, + "vegetables": {"carrot", "broccoli", "spinach"}, + } + fmt.Printf(" 切片值: %v\n", groups) + + // 映射值 + nested := map[string]map[string]int{ + "math": {"Alice": 95, "Bob": 87}, + "english": {"Alice": 92, "Bob": 89}, + } + fmt.Printf(" 嵌套映射: %v\n", nested) + + fmt.Println() +} + +// demonstrateMapOperations 演示映射的基本操作 +func demonstrateMapOperations() { + fmt.Println("3. 映射的基本操作:") + + // 创建映射 + inventory := make(map[string]int) + + // 添加/更新元素 + fmt.Printf(" 添加/更新元素:\n") + inventory["apples"] = 50 + inventory["bananas"] = 30 + inventory["oranges"] = 25 + fmt.Printf(" 添加水果: %v\n", inventory) + + // 更新现有元素 + inventory["apples"] = 45 + fmt.Printf(" 更新苹果数量: %v\n", inventory) + + // 读取元素 + fmt.Printf(" 读取元素:\n") + apples := inventory["apples"] + fmt.Printf(" 苹果数量: %d\n", apples) + + // 检查键是否存在 + fmt.Printf(" 检查键是否存在:\n") + value, exists := inventory["bananas"] + fmt.Printf(" bananas存在: %t, 值: %d\n", exists, value) + + value, exists = inventory["grapes"] + fmt.Printf(" grapes存在: %t, 值: %d\n", exists, value) + + // 删除元素 + fmt.Printf(" 删除元素:\n") + fmt.Printf(" 删除前: %v\n", inventory) + delete(inventory, "oranges") + fmt.Printf(" 删除oranges后: %v\n", inventory) + + // 删除不存在的键(安全操作) + delete(inventory, "grapes") + fmt.Printf(" 删除不存在的键后: %v\n", inventory) + + // 批量操作 + fmt.Printf(" 批量操作:\n") + newItems := map[string]int{ + "pears": 20, + "grapes": 35, + "kiwis": 15, + } + + // 批量添加 + for key, value := range newItems { + inventory[key] = value + } + fmt.Printf(" 批量添加后: %v\n", inventory) + + // 清空映射 + fmt.Printf(" 清空映射:\n") + for key := range inventory { + delete(inventory, key) + } + fmt.Printf(" 清空后: %v (len=%d)\n", inventory, len(inventory)) + + fmt.Println() +}// + demonstrateMapIteration 演示映射的遍历 +func demonstrateMapIteration() { + fmt.Println("4. 映射的遍历:") + + students := map[string]int{ + "Alice": 95, + "Bob": 87, + "Charlie": 92, + "David": 78, + "Eve": 89, + } + + // 遍历键值对 + fmt.Printf(" 遍历键值对:\n") + for name, score := range students { + fmt.Printf(" %s: %d\n", name, score) + } + + // 只遍历键 + fmt.Printf(" 只遍历键:\n") + for name := range students { + fmt.Printf(" 学生: %s\n", name) + } + + // 只遍历值 + fmt.Printf(" 只遍历值:\n") + for _, score := range students { + fmt.Printf(" 分数: %d\n", score) + } + + // 注意:映射遍历顺序是随机的 + fmt.Printf(" 注意: 映射遍历顺序是随机的\n") + fmt.Printf(" 多次遍历可能得到不同的顺序\n") + + // 有序遍历 + fmt.Printf(" 有序遍历(按键排序):\n") + + // 收集所有键 + var names []string + for name := range students { + names = append(names, name) + } + + // 排序键 + sort.Strings(names) + + // 按排序后的键遍历 + for _, name := range names { + fmt.Printf(" %s: %d\n", name, students[name]) + } + + // 按值排序遍历 + fmt.Printf(" 按分数排序遍历:\n") + + type StudentScore struct { + Name string + Score int + } + + var studentList []StudentScore + for name, score := range students { + studentList = append(studentList, StudentScore{name, score}) + } + + // 按分数排序 + sort.Slice(studentList, func(i, j int) bool { + return studentList[i].Score > studentList[j].Score + }) + + for _, student := range studentList { + fmt.Printf(" %s: %d\n", student.Name, student.Score) + } + + fmt.Println() +} + +// demonstrateAdvancedMapOperations 演示映射的高级操作 +func demonstrateAdvancedMapOperations() { + fmt.Println("5. 映射的高级操作:") + + // 映射合并 + fmt.Printf(" 映射合并:\n") + map1 := map[string]int{"a": 1, "b": 2, "c": 3} + map2 := map[string]int{"c": 30, "d": 4, "e": 5} + + fmt.Printf(" map1: %v\n", map1) + fmt.Printf(" map2: %v\n", map2) + + // 合并 map2 到 map1 + for key, value := range map2 { + map1[key] = value + } + fmt.Printf(" 合并后: %v\n", map1) + + // 映射复制 + fmt.Printf(" 映射复制:\n") + original := map[string]int{"x": 10, "y": 20, "z": 30} + copied := make(map[string]int) + + for key, value := range original { + copied[key] = value + } + + fmt.Printf(" 原映射: %v\n", original) + fmt.Printf(" 复制映射: %v\n", copied) + + // 修改复制的映射不影响原映射 + copied["x"] = 99 + fmt.Printf(" 修改复制后:\n") + fmt.Printf(" 原映射: %v\n", original) + fmt.Printf(" 复制映射: %v\n", copied) + + // 映射过滤 + fmt.Printf(" 映射过滤:\n") + allScores := map[string]int{ + "Alice": 95, "Bob": 67, "Charlie": 92, + "David": 45, "Eve": 89, "Frank": 78, + } + + // 过滤出及格的学生 + passing := make(map[string]int) + for name, score := range allScores { + if score >= 70 { + passing[name] = score + } + } + + fmt.Printf(" 所有分数: %v\n", allScores) + fmt.Printf(" 及格学生: %v\n", passing) + + // 映射转换 + fmt.Printf(" 映射转换:\n") + temperatures := map[string]float64{ + "北京": 25.5, + "上海": 28.3, + "广州": 32.1, + } + + // 摄氏度转华氏度 + fahrenheit := make(map[string]float64) + for city, celsius := range temperatures { + fahrenheit[city] = celsius*9/5 + 32 + } + + fmt.Printf(" 摄氏度: %v\n", temperatures) + fmt.Printf(" 华氏度: %v\n", fahrenheit) + + // 反转映射(键值互换) + fmt.Printf(" 反转映射:\n") + codeToName := map[int]string{ + 1: "Alice", + 2: "Bob", + 3: "Charlie", + } + + nameToCode := make(map[string]int) + for code, name := range codeToName { + nameToCode[name] = code + } + + fmt.Printf(" 代码到姓名: %v\n", codeToName) + fmt.Printf(" 姓名到代码: %v\n", nameToCode) + + fmt.Println() +} + +// demonstrateMapAsSet 演示映射作为集合使用 +func demonstrateMapAsSet() { + fmt.Println("6. 映射作为集合使用:") + + fmt.Printf(" Go 没有内置的集合类型,可以用 map[T]bool 模拟\n") + + // 创建集合 + fmt.Printf(" 创建集合:\n") + fruits := make(map[string]bool) + + // 添加元素 + fruits["apple"] = true + fruits["banana"] = true + fruits["cherry"] = true + + fmt.Printf(" 水果集合: %v\n", getKeys(fruits)) + + // 检查元素是否存在 + fmt.Printf(" 检查元素存在:\n") + fmt.Printf(" apple 存在: %t\n", fruits["apple"]) + fmt.Printf(" grape 存在: %t\n", fruits["grape"]) + + // 删除元素 + delete(fruits, "banana") + fmt.Printf(" 删除 banana 后: %v\n", getKeys(fruits)) + + // 集合运算 + fmt.Printf(" 集合运算:\n") + set1 := map[string]bool{"a": true, "b": true, "c": true} + set2 := map[string]bool{"b": true, "c": true, "d": true} + + fmt.Printf(" 集合1: %v\n", getKeys(set1)) + fmt.Printf(" 集合2: %v\n", getKeys(set2)) + + // 并集 + union := make(map[string]bool) + for key := range set1 { + union[key] = true + } + for key := range set2 { + union[key] = true + } + fmt.Printf(" 并集: %v\n", getKeys(union)) + + // 交集 + intersection := make(map[string]bool) + for key := range set1 { + if set2[key] { + intersection[key] = true + } + } + fmt.Printf(" 交集: %v\n", getKeys(intersection)) + + // 差集 (set1 - set2) + difference := make(map[string]bool) + for key := range set1 { + if !set2[key] { + difference[key] = true + } + } + fmt.Printf(" 差集(1-2): %v\n", getKeys(difference)) + + // 使用 map[T]struct{} 更节省内存 + fmt.Printf(" 使用 struct{} 作为值更节省内存:\n") + efficientSet := make(map[string]struct{}) + efficientSet["item1"] = struct{}{} + efficientSet["item2"] = struct{}{} + + // 检查存在 + _, exists := efficientSet["item1"] + fmt.Printf(" item1 存在: %t\n", exists) + + fmt.Println() +} + +// demonstratePracticalApplications 演示映射的实际应用 +func demonstratePracticalApplications() { + fmt.Println("7. 映射的实际应用:") + + // 应用1: 计数器 + fmt.Printf(" 应用1 - 字符频率统计:\n") + text := "hello world" + frequency := make(map[rune]int) + + for _, char := range text { + if char != ' ' { // 忽略空格 + frequency[char]++ + } + } + + fmt.Printf(" 文本: \"%s\"\n", text) + fmt.Printf(" 字符频率:\n") + for char, count := range frequency { + fmt.Printf(" '%c': %d\n", char, count) + } + + // 应用2: 缓存系统 + fmt.Printf(" 应用2 - 简单缓存:\n") + cache := NewCache() + + // 设置缓存 + cache.Set("user:123", "Alice") + cache.Set("user:456", "Bob") + + // 获取缓存 + if value, found := cache.Get("user:123"); found { + fmt.Printf(" 缓存命中: user:123 = %s\n", value) + } + + if value, found := cache.Get("user:789"); found { + fmt.Printf(" 缓存命中: user:789 = %s\n", value) + } else { + fmt.Printf(" 缓存未命中: user:789\n") + } + + // 应用3: 配置管理 + fmt.Printf(" 应用3 - 配置管理:\n") + config := map[string]interface{}{ + "database.host": "localhost", + "database.port": 5432, + "database.ssl": true, + "app.debug": false, + "app.max_connections": 100, + } + + fmt.Printf(" 配置项:\n") + for key, value := range config { + fmt.Printf(" %s: %v (%T)\n", key, value, value) + } + + // 应用4: 分组统计 + fmt.Printf(" 应用4 - 学生成绩分组:\n") + students := []struct { + Name string + Grade string + Score int + }{ + {"Alice", "A", 95}, + {"Bob", "B", 87}, + {"Charlie", "A", 92}, + {"David", "C", 78}, + {"Eve", "B", 89}, + {"Frank", "A", 96}, + } + + // 按等级分组 + gradeGroups := make(map[string][]string) + gradeScores := make(map[string][]int) + + for _, student := range students { + gradeGroups[student.Grade] = append(gradeGroups[student.Grade], student.Name) + gradeScores[student.Grade] = append(gradeScores[student.Grade], student.Score) + } + + fmt.Printf(" 按等级分组:\n") + for grade, names := range gradeGroups { + scores := gradeScores[grade] + avg := average(scores) + fmt.Printf(" %s级: %v (平均分: %.1f)\n", grade, names, avg) + } + + // 应用5: 索引构建 + fmt.Printf(" 应用5 - 倒排索引:\n") + documents := map[string]string{ + "doc1": "go programming language", + "doc2": "python programming tutorial", + "doc3": "go web development", + "doc4": "java programming guide", + } + + // 构建倒排索引 + index := buildInvertedIndex(documents) + + fmt.Printf(" 文档:\n") + for docId, content := range documents { + fmt.Printf(" %s: %s\n", docId, content) + } + + fmt.Printf(" 倒排索引:\n") + for word, docIds := range index { + fmt.Printf(" %s: %v\n", word, docIds) + } + + // 搜索 + searchWord := "programming" + if docIds, found := index[searchWord]; found { + fmt.Printf(" 搜索 '%s': 找到文档 %v\n", searchWord, docIds) + } + + fmt.Println() +} + +// ========== 辅助函数和类型 ========== + +// 获取映射的所有键 +func getKeys(m map[string]bool) []string { + var keys []string + for key := range m { + keys = append(keys, key) + } + sort.Strings(keys) // 排序以便输出一致 + return keys +} + +// 计算平均值 +func average(numbers []int) float64 { + if len(numbers) == 0 { + return 0 + } + + sum := 0 + for _, num := range numbers { + sum += num + } + + return float64(sum) / float64(len(numbers)) +} + +// 简单缓存实现 +type Cache struct { + data map[string]string +} + +func NewCache() *Cache { + return &Cache{ + data: make(map[string]string), + } +} + +func (c *Cache) Set(key, value string) { + c.data[key] = value +} + +func (c *Cache) Get(key string) (string, bool) { + value, exists := c.data[key] + return value, exists +} + +func (c *Cache) Delete(key string) { + delete(c.data, key) +} + +func (c *Cache) Clear() { + c.data = make(map[string]string) +} + +// 构建倒排索引 +func buildInvertedIndex(documents map[string]string) map[string][]string { + index := make(map[string][]string) + + for docId, content := range documents { + words := strings.Fields(content) + for _, word := range words { + // 检查是否已经包含该文档 + found := false + for _, existingDocId := range index[word] { + if existingDocId == docId { + found = true + break + } + } + + if !found { + index[word] = append(index[word], docId) + } + } + } + + return index +} + +// 需要导入 strings 包 +import "strings" + +/* +运行这个程序: +go run 03-maps.go + +学习要点: +1. 映射是键值对的无序集合,键必须是可比较类型 +2. 映射是引用类型,零值是 nil +3. 映射的长度是动态的,可以增长和缩减 +4. 映射不能直接比较,只能与 nil 比较 +5. 映射的遍历顺序是随机的 + +映射的特性: +1. 键唯一性:每个键只能对应一个值 +2. 动态大小:可以动态添加和删除键值对 +3. 快速查找:平均 O(1) 时间复杂度 +4. 无序性:不保证遍历顺序 +5. 引用语义:赋值和传参传递引用 + +创建映射的方式: +1. 字面量:map[KeyType]ValueType{key: value} +2. make函数:make(map[KeyType]ValueType) +3. 空字面量:map[KeyType]ValueType{} + +基本操作: +1. 添加/更新:m[key] = value +2. 读取:value := m[key] +3. 检查存在:value, ok := m[key] +4. 删除:delete(m, key) +5. 长度:len(m) + +可比较的键类型: +1. 基本类型:bool, 数值类型, string +2. 指针类型 +3. 数组类型(元素可比较) +4. 结构体类型(所有字段可比较) + +不可比较的键类型: +1. 切片类型 +2. 映射类型 +3. 函数类型 +4. 包含不可比较字段的结构体 + +常见应用场景: +1. 缓存和查找表 +2. 计数器和统计 +3. 配置管理 +4. 索引构建 +5. 集合操作 +6. 分组和聚合 + +最佳实践: +1. 检查键是否存在避免零值混淆 +2. 使用 make 创建空映射而不是 nil 映射 +3. 大映射考虑预分配容量 +4. 使用 map[T]struct{} 实现集合节省内存 +5. 注意映射的并发安全问题 + +注意事项: +1. nil 映射不能写入,只能读取 +2. 映射不是并发安全的 +3. 映射的遍历顺序是随机的 +4. 删除不存在的键是安全操作 +5. 映射的零值是 nil,不是空映射 +*/ \ No newline at end of file diff --git a/golang-learning/04-data-structures/04-structs.go b/golang-learning/04-data-structures/04-structs.go new file mode 100644 index 0000000..0928e08 --- /dev/null +++ b/golang-learning/04-data-structures/04-structs.go @@ -0,0 +1,892 @@ +/* +04-structs.go - Go 语言结构体详解 + +学习目标: +1. 理解结构体的概念和作用 +2. 掌握结构体的定义和使用 +3. 学会结构体的初始化方式 +4. 了解结构体的嵌入和组合 +5. 掌握结构体的实际应用场景 + +知识点: +- 结构体的定义和特性 +- 结构体的初始化方式 +- 结构体字段的访问和修改 +- 结构体的方法 +- 结构体的嵌入和组合 +- 结构体的标签 +- 结构体的比较和拷贝 +*/ + +package main + +import ( + "fmt" +) + +func main() { + fmt.Println("=== Go 语言结构体详解 ===\n") + + // 演示结构体的基本概念 + demonstrateBasicStructs() + + // 演示结构体的初始化 + demonstrateStructInitialization() + + // 演示结构体的方法 + demonstrateStructMethods() + + // 演示结构体的嵌入 + demonstrateStructEmbedding() + + // 演示结构体的标签 + demonstrateStructTags() + + // 演示结构体的高级特性 + demonstrateAdvancedStructFeatures() + + // 演示结构体的实际应用 + demonstratePracticalApplications() +} + +// demonstrateBasicStructs 演示结构体的基本概念 +func demonstrateBasicStructs() { + fmt.Println("1. 结构体的基本概念:") + + // 结构体的基本特性 + fmt.Printf(" 结构体的基本特性:\n") + fmt.Printf(" - 用户自定义的复合数据类型\n") + fmt.Printf(" - 将相关数据组织在一起\n") + fmt.Printf(" - 值类型,赋值和传参会拷贝\n") + fmt.Printf(" - 可以有方法\n") + fmt.Printf(" - 支持嵌入和组合\n") + + // 基本结构体示例 + fmt.Printf(" 基本结构体示例:\n") + + // 创建结构体实例 + var p Person + fmt.Printf(" 零值结构体: %+v\n", p) + + // 设置字段值 + p.Name = "Alice" + p.Age = 25 + p.Email = "alice@example.com" + fmt.Printf(" 设置字段后: %+v\n", p) + + // 访问字段 + fmt.Printf(" 姓名: %s\n", p.Name) + fmt.Printf(" 年龄: %d\n", p.Age) + fmt.Printf(" 邮箱: %s\n", p.Email) + + // 结构体比较 + fmt.Printf(" 结构体比较:\n") + p1 := Person{Name: "Bob", Age: 30, Email: "bob@example.com"} + p2 := Person{Name: "Bob", Age: 30, Email: "bob@example.com"} + p3 := Person{Name: "Charlie", Age: 25, Email: "charlie@example.com"} + + fmt.Printf(" p1 == p2: %t\n", p1 == p2) + fmt.Printf(" p1 == p3: %t\n", p1 == p3) + + // 结构体作为值类型 + fmt.Printf(" 结构体作为值类型:\n") + original := Person{Name: "David", Age: 35, Email: "david@example.com"} + copied := original + + fmt.Printf(" 原结构体: %+v\n", original) + fmt.Printf(" 拷贝结构体: %+v\n", copied) + + // 修改拷贝不影响原结构体 + copied.Age = 40 + fmt.Printf(" 修改拷贝后:\n") + fmt.Printf(" 原结构体: %+v\n", original) + fmt.Printf(" 拷贝结构体: %+v\n", copied) + + fmt.Println() +} + +// demonstrateStructInitialization 演示结构体的初始化 +func demonstrateStructInitialization() { + fmt.Println("2. 结构体的初始化:") + + // 方式1: 零值初始化 + fmt.Printf(" 方式1 - 零值初始化:\n") + var p1 Person + fmt.Printf(" var p1 Person: %+v\n", p1) + + // 方式2: 字段名初始化 + fmt.Printf(" 方式2 - 字段名初始化:\n") + p2 := Person{ + Name: "Alice", + Age: 25, + Email: "alice@example.com", + } + fmt.Printf(" 字段名初始化: %+v\n", p2) + + // 方式3: 位置初始化(不推荐) + fmt.Printf(" 方式3 - 位置初始化:\n") + p3 := Person{"Bob", 30, "bob@example.com"} + fmt.Printf(" 位置初始化: %+v\n", p3) + + // 方式4: 部分初始化 + fmt.Printf(" 方式4 - 部分初始化:\n") + p4 := Person{ + Name: "Charlie", + Age: 35, + // Email 使用零值 + } + fmt.Printf(" 部分初始化: %+v\n", p4) + + // 方式5: 使用 new 函数 + fmt.Printf(" 方式5 - 使用 new 函数:\n") + p5 := new(Person) + p5.Name = "David" + p5.Age = 40 + fmt.Printf(" new(Person): %+v\n", *p5) + + // 方式6: 结构体字面量的地址 + fmt.Printf(" 方式6 - 结构体字面量的地址:\n") + p6 := &Person{ + Name: "Eve", + Age: 28, + Email: "eve@example.com", + } + fmt.Printf(" &Person{...}: %+v\n", *p6) + + // 复杂结构体初始化 + fmt.Printf(" 复杂结构体初始化:\n") + + addr := Address{ + Street: "123 Main St", + City: "New York", + Country: "USA", + ZipCode: "10001", + } + + employee := Employee{ + Person: Person{ + Name: "Frank", + Age: 32, + Email: "frank@company.com", + }, + ID: 1001, + Position: "Software Engineer", + Salary: 75000, + Address: addr, + } + + fmt.Printf(" 复杂结构体: %+v\n", employee) + + // 匿名结构体 + fmt.Printf(" 匿名结构体:\n") + point := struct { + X, Y int + }{ + X: 10, + Y: 20, + } + fmt.Printf(" 匿名结构体: %+v\n", point) + + fmt.Println() +}// +demonstrateStructMethods 演示结构体的方法 +func demonstrateStructMethods() { + fmt.Println("3. 结构体的方法:") + + // 创建结构体实例 + fmt.Printf(" 结构体方法调用:\n") + + rect := Rectangle{Width: 10, Height: 5} + fmt.Printf(" 矩形: %+v\n", rect) + fmt.Printf(" 面积: %.2f\n", rect.Area()) + fmt.Printf(" 周长: %.2f\n", rect.Perimeter()) + + // 值接收者 vs 指针接收者 + fmt.Printf(" 值接收者 vs 指针接收者:\n") + + // 值接收者方法 + fmt.Printf(" 调用值接收者方法:\n") + info := rect.String() + fmt.Printf(" String(): %s\n", info) + + // 指针接收者方法 + fmt.Printf(" 调用指针接收者方法:\n") + fmt.Printf(" 修改前: %+v\n", rect) + rect.Scale(2.0) + fmt.Printf(" Scale(2.0)后: %+v\n", rect) + + // 方法链调用 + fmt.Printf(" 方法链调用:\n") + rect2 := Rectangle{Width: 3, Height: 4} + fmt.Printf(" 原矩形: %+v\n", rect2) + + result := rect2.SetWidth(6).SetHeight(8) + fmt.Printf(" 链式调用后: %+v\n", *result) + + // 结构体方法作为函数值 + fmt.Printf(" 结构体方法作为函数值:\n") + + circle := Circle{Radius: 5} + areaFunc := circle.Area + circumferenceFunc := circle.Circumference + + fmt.Printf(" 圆形: %+v\n", circle) + fmt.Printf(" 方法值调用 - 面积: %.2f\n", areaFunc()) + fmt.Printf(" 方法值调用 - 周长: %.2f\n", circumferenceFunc()) + + fmt.Println() +} + +// demonstrateStructEmbedding 演示结构体的嵌入 +func demonstrateStructEmbedding() { + fmt.Println("4. 结构体的嵌入:") + + // 具名嵌入 + fmt.Printf(" 具名嵌入:\n") + + emp := Employee{ + Person: Person{ + Name: "Alice", + Age: 30, + Email: "alice@company.com", + }, + ID: 1001, + Position: "Manager", + Salary: 80000, + } + + fmt.Printf(" 员工信息: %+v\n", emp) + fmt.Printf(" 访问嵌入字段: %s, %d\n", emp.Person.Name, emp.Person.Age) + + // 匿名嵌入 + fmt.Printf(" 匿名嵌入:\n") + + manager := Manager{ + Person: Person{ + Name: "Bob", + Age: 35, + Email: "bob@company.com", + }, + Department: "Engineering", + TeamSize: 10, + } + + fmt.Printf(" 经理信息: %+v\n", manager) + + // 直接访问嵌入字段 + fmt.Printf(" 直接访问嵌入字段:\n") + fmt.Printf(" 姓名: %s\n", manager.Name) // 等同于 manager.Person.Name + fmt.Printf(" 年龄: %d\n", manager.Age) // 等同于 manager.Person.Age + fmt.Printf(" 邮箱: %s\n", manager.Email) // 等同于 manager.Person.Email + + // 调用嵌入类型的方法 + fmt.Printf(" 调用嵌入类型的方法:\n") + info := manager.GetInfo() // Person 的方法 + fmt.Printf(" GetInfo(): %s\n", info) + + // 方法重写 + fmt.Printf(" 方法重写:\n") + + student := Student{ + Person: Person{ + Name: "Charlie", + Age: 20, + Email: "charlie@university.edu", + }, + StudentID: "S12345", + Major: "Computer Science", + GPA: 3.8, + } + + fmt.Printf(" 学生信息: %+v\n", student) + + // Student 重写了 GetInfo 方法 + studentInfo := student.GetInfo() + fmt.Printf(" Student.GetInfo(): %s\n", studentInfo) + + // 仍然可以访问原始方法 + personInfo := student.Person.GetInfo() + fmt.Printf(" Person.GetInfo(): %s\n", personInfo) + + // 多重嵌入 + fmt.Printf(" 多重嵌入:\n") + + contact := ContactInfo{ + Phone: "123-456-7890", + Address: "123 Main St", + } + + fullEmployee := FullEmployee{ + Person: Person{Name: "David", Age: 28, Email: "david@company.com"}, + Employee: Employee{ID: 1002, Position: "Developer", Salary: 70000}, + ContactInfo: contact, + } + + fmt.Printf(" 完整员工信息: %+v\n", fullEmployee) + fmt.Printf(" 访问各层字段:\n") + fmt.Printf(" 姓名: %s\n", fullEmployee.Person.Name) + fmt.Printf(" 职位: %s\n", fullEmployee.Employee.Position) + fmt.Printf(" 电话: %s\n", fullEmployee.ContactInfo.Phone) + + fmt.Println() +} + +// demonstrateStructTags 演示结构体的标签 +func demonstrateStructTags() { + fmt.Println("5. 结构体的标签:") + + fmt.Printf(" 结构体标签用于元数据,常用于序列化、验证等\n") + + // 创建带标签的结构体实例 + user := User{ + ID: 1, + Username: "alice", + Email: "alice@example.com", + FullName: "Alice Smith", + IsActive: true, + CreatedAt: time.Now(), + } + + fmt.Printf(" 用户信息: %+v\n", user) + + // 使用反射读取标签 + fmt.Printf(" 使用反射读取标签:\n") + + userType := reflect.TypeOf(user) + for i := 0; i < userType.NumField(); i++ { + field := userType.Field(i) + jsonTag := field.Tag.Get("json") + dbTag := field.Tag.Get("db") + validateTag := field.Tag.Get("validate") + + fmt.Printf(" 字段 %s:\n", field.Name) + if jsonTag != "" { + fmt.Printf(" json: %s\n", jsonTag) + } + if dbTag != "" { + fmt.Printf(" db: %s\n", dbTag) + } + if validateTag != "" { + fmt.Printf(" validate: %s\n", validateTag) + } + } + + // 标签的实际应用 + fmt.Printf(" 标签的实际应用:\n") + fmt.Printf(" - JSON 序列化/反序列化\n") + fmt.Printf(" - 数据库 ORM 映射\n") + fmt.Printf(" - 数据验证\n") + fmt.Printf(" - 配置文件解析\n") + fmt.Printf(" - API 文档生成\n") + + fmt.Println() +} + +// demonstrateAdvancedStructFeatures 演示结构体的高级特性 +func demonstrateAdvancedStructFeatures() { + fmt.Println("6. 结构体的高级特性:") + + // 结构体作为映射的值 + fmt.Printf(" 结构体作为映射的值:\n") + + users := map[string]Person{ + "alice": {Name: "Alice", Age: 25, Email: "alice@example.com"}, + "bob": {Name: "Bob", Age: 30, Email: "bob@example.com"}, + } + + fmt.Printf(" 用户映射: %+v\n", users) + + // 结构体切片 + fmt.Printf(" 结构体切片:\n") + + people := []Person{ + {Name: "Alice", Age: 25, Email: "alice@example.com"}, + {Name: "Bob", Age: 30, Email: "bob@example.com"}, + {Name: "Charlie", Age: 35, Email: "charlie@example.com"}, + } + + fmt.Printf(" 人员列表:\n") + for i, person := range people { + fmt.Printf(" %d: %s (%d岁)\n", i+1, person.Name, person.Age) + } + + // 结构体指针切片 + fmt.Printf(" 结构体指针切片:\n") + + var personPtrs []*Person + for i := range people { + personPtrs = append(personPtrs, &people[i]) + } + + // 修改通过指针 + personPtrs[0].Age = 26 + fmt.Printf(" 修改后的第一个人: %+v\n", people[0]) + + // 空结构体 + fmt.Printf(" 空结构体:\n") + + type Empty struct{} + + var empty Empty + fmt.Printf(" 空结构体大小: %d 字节\n", reflect.TypeOf(empty).Size()) + fmt.Printf(" 空结构体常用于信号传递和集合实现\n") + + // 使用空结构体实现集合 + set := make(map[string]struct{}) + set["item1"] = struct{}{} + set["item2"] = struct{}{} + + fmt.Printf(" 集合: %v\n", getMapKeys(set)) + + // 结构体的内存对齐 + fmt.Printf(" 结构体的内存对齐:\n") + + type Aligned struct { + a bool // 1 byte + b int64 // 8 bytes + c bool // 1 byte + } + + type Optimized struct { + b int64 // 8 bytes + a bool // 1 byte + c bool // 1 byte + } + + fmt.Printf(" 未优化结构体大小: %d 字节\n", reflect.TypeOf(Aligned{}).Size()) + fmt.Printf(" 优化后结构体大小: %d 字节\n", reflect.TypeOf(Optimized{}).Size()) + + fmt.Println() +} + +// demonstratePracticalApplications 演示结构体的实际应用 +func demonstratePracticalApplications() { + fmt.Println("7. 结构体的实际应用:") + + // 应用1: 配置管理 + fmt.Printf(" 应用1 - 配置管理:\n") + + config := Config{ + Server: ServerConfig{ + Host: "localhost", + Port: 8080, + SSL: false, + }, + Database: DatabaseConfig{ + Host: "localhost", + Port: 5432, + Username: "admin", + Password: "secret", + Database: "myapp", + }, + Cache: CacheConfig{ + Enabled: true, + TTL: 300, + MaxSize: 1000, + }, + } + + fmt.Printf(" 应用配置: %+v\n", config) + fmt.Printf(" 服务器地址: %s:%d\n", config.Server.Host, config.Server.Port) + fmt.Printf(" 数据库连接: %s@%s:%d/%s\n", + config.Database.Username, config.Database.Host, + config.Database.Port, config.Database.Database) + + // 应用2: HTTP 请求/响应 + fmt.Printf(" 应用2 - HTTP 请求/响应:\n") + + request := HTTPRequest{ + Method: "POST", + URL: "/api/users", + Headers: map[string]string{ + "Content-Type": "application/json", + "Authorization": "Bearer token123", + }, + Body: `{"name": "Alice", "email": "alice@example.com"}`, + } + + response := HTTPResponse{ + StatusCode: 201, + Headers: map[string]string{ + "Content-Type": "application/json", + }, + Body: `{"id": 1, "name": "Alice", "email": "alice@example.com"}`, + } + + fmt.Printf(" HTTP 请求: %+v\n", request) + fmt.Printf(" HTTP 响应: %+v\n", response) + + // 应用3: 数据传输对象 (DTO) + fmt.Printf(" 应用3 - 数据传输对象:\n") + + userDTO := UserDTO{ + ID: 1, + Username: "alice", + Profile: ProfileDTO{ + FirstName: "Alice", + LastName: "Smith", + Bio: "Software Developer", + }, + Settings: SettingsDTO{ + Theme: "dark", + Notifications: true, + Language: "en", + }, + } + + fmt.Printf(" 用户 DTO: %+v\n", userDTO) + + // 应用4: 事件系统 + fmt.Printf(" 应用4 - 事件系统:\n") + + events := []Event{ + { + ID: 1, + Type: "user.created", + Timestamp: time.Now(), + Data: map[string]interface{}{ + "user_id": 123, + "email": "user@example.com", + }, + }, + { + ID: 2, + Type: "order.placed", + Timestamp: time.Now(), + Data: map[string]interface{}{ + "order_id": 456, + "amount": 99.99, + }, + }, + } + + fmt.Printf(" 事件列表:\n") + for _, event := range events { + fmt.Printf(" 事件 %d: %s (时间: %s)\n", + event.ID, event.Type, event.Timestamp.Format("15:04:05")) + fmt.Printf(" 数据: %+v\n", event.Data) + } + + // 应用5: 状态机 + fmt.Printf(" 应用5 - 状态机:\n") + + fsm := StateMachine{ + CurrentState: "idle", + States: map[string]State{ + "idle": { + Name: "idle", + Transitions: map[string]string{ + "start": "running", + }, + }, + "running": { + Name: "running", + Transitions: map[string]string{ + "pause": "paused", + "stop": "idle", + }, + }, + "paused": { + Name: "paused", + Transitions: map[string]string{ + "resume": "running", + "stop": "idle", + }, + }, + }, + } + + fmt.Printf(" 状态机当前状态: %s\n", fsm.CurrentState) + + // 状态转换 + if newState, ok := fsm.States[fsm.CurrentState].Transitions["start"]; ok { + fsm.CurrentState = newState + fmt.Printf(" 执行 'start' 转换后状态: %s\n", fsm.CurrentState) + } + + fmt.Println() +}// === +======= 结构体定义 ========== + +// 基本结构体 +type Person struct { + Name string + Age int + Email string +} + +// Person 的方法 +func (p Person) GetInfo() string { + return fmt.Sprintf("%s (%d岁) - %s", p.Name, p.Age, p.Email) +} + +func (p Person) IsAdult() bool { + return p.Age >= 18 +} + +// 地址结构体 +type Address struct { + Street string + City string + Country string + ZipCode string +} + +// 员工结构体(具名嵌入) +type Employee struct { + Person Person // 具名嵌入 + ID int + Position string + Salary float64 + Address Address +} + +// 几何图形结构体 +type Rectangle struct { + Width float64 + Height float64 +} + +// Rectangle 的方法 +func (r Rectangle) Area() float64 { + return r.Width * r.Height +} + +func (r Rectangle) Perimeter() float64 { + return 2 * (r.Width + r.Height) +} + +func (r Rectangle) String() string { + return fmt.Sprintf("Rectangle(%.1f x %.1f)", r.Width, r.Height) +} + +// 指针接收者方法 +func (r *Rectangle) Scale(factor float64) { + r.Width *= factor + r.Height *= factor +} + +// 方法链 +func (r *Rectangle) SetWidth(width float64) *Rectangle { + r.Width = width + return r +} + +func (r *Rectangle) SetHeight(height float64) *Rectangle { + r.Height = height + return r +} + +// 圆形结构体 +type Circle struct { + Radius float64 +} + +func (c Circle) Area() float64 { + return 3.14159 * c.Radius * c.Radius +} + +func (c Circle) Circumference() float64 { + return 2 * 3.14159 * c.Radius +} + +// 经理结构体(匿名嵌入) +type Manager struct { + Person // 匿名嵌入 + Department string + TeamSize int +} + +// 学生结构体(方法重写) +type Student struct { + Person + StudentID string + Major string + GPA float64 +} + +// 重写 Person 的 GetInfo 方法 +func (s Student) GetInfo() string { + return fmt.Sprintf("学生 %s (%d岁) - %s专业, 学号: %s, GPA: %.1f", + s.Name, s.Age, s.Major, s.StudentID, s.GPA) +} + +// 联系信息结构体 +type ContactInfo struct { + Phone string + Address string +} + +// 完整员工结构体(多重嵌入) +type FullEmployee struct { + Person + Employee + ContactInfo +} + +// 带标签的结构体 +type User struct { + ID int `json:"id" db:"id" validate:"required"` + Username string `json:"username" db:"username" validate:"required,min=3,max=20"` + Email string `json:"email" db:"email" validate:"required,email"` + FullName string `json:"full_name" db:"full_name"` + IsActive bool `json:"is_active" db:"is_active"` + CreatedAt time.Time `json:"created_at" db:"created_at"` +} + +// 配置结构体 +type Config struct { + Server ServerConfig + Database DatabaseConfig + Cache CacheConfig +} + +type ServerConfig struct { + Host string + Port int + SSL bool +} + +type DatabaseConfig struct { + Host string + Port int + Username string + Password string + Database string +} + +type CacheConfig struct { + Enabled bool + TTL int + MaxSize int +} + +// HTTP 请求/响应结构体 +type HTTPRequest struct { + Method string + URL string + Headers map[string]string + Body string +} + +type HTTPResponse struct { + StatusCode int + Headers map[string]string + Body string +} + +// 数据传输对象 +type UserDTO struct { + ID int `json:"id"` + Username string `json:"username"` + Profile ProfileDTO `json:"profile"` + Settings SettingsDTO `json:"settings"` +} + +type ProfileDTO struct { + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + Bio string `json:"bio"` +} + +type SettingsDTO struct { + Theme string `json:"theme"` + Notifications bool `json:"notifications"` + Language string `json:"language"` +} + +// 事件结构体 +type Event struct { + ID int `json:"id"` + Type string `json:"type"` + Timestamp time.Time `json:"timestamp"` + Data map[string]interface{} `json:"data"` +} + +// 状态机结构体 +type StateMachine struct { + CurrentState string + States map[string]State +} + +type State struct { + Name string + Transitions map[string]string +} + +// ========== 辅助函数 ========== + +// 获取映射的键 +func getMapKeys(m map[string]struct{}) []string { + var keys []string + for key := range m { + keys = append(keys, key) + } + return keys +} + +/* +运行这个程序: +go run 04-structs.go + +学习要点: +1. 结构体是用户自定义的复合数据类型 +2. 结构体是值类型,赋值和传参会拷贝整个结构体 +3. 结构体可以有方法,支持面向对象编程 +4. 结构体支持嵌入和组合,实现代码复用 +5. 结构体标签用于元数据,常用于序列化和验证 + +结构体的特性: +1. 类型安全:编译时检查字段类型 +2. 内存效率:字段在内存中连续存储 +3. 可比较性:所有字段可比较时结构体可比较 +4. 零值:所有字段都为零值的结构体 +5. 方法:可以定义方法操作结构体 + +初始化方式: +1. 零值初始化:var s Struct +2. 字段名初始化:Struct{Field: value} +3. 位置初始化:Struct{value1, value2} +4. 部分初始化:Struct{Field1: value} +5. 指针初始化:&Struct{} 或 new(Struct) + +结构体嵌入: +1. 具名嵌入:显式字段名 +2. 匿名嵌入:直接访问嵌入字段 +3. 方法继承:自动获得嵌入类型的方法 +4. 方法重写:可以重写嵌入类型的方法 + +结构体标签: +1. 元数据:为字段提供额外信息 +2. 反射访问:通过反射读取标签 +3. 常见用途:JSON、数据库、验证 +4. 格式:`key:"value" key2:"value2"` + +最佳实践: +1. 字段名使用驼峰命名 +2. 导出字段首字母大写 +3. 合理组织字段顺序(内存对齐) +4. 使用指针接收者修改结构体 +5. 适当使用嵌入实现代码复用 + +常见应用场景: +1. 数据模型和实体 +2. 配置管理 +3. API 请求/响应 +4. 事件和消息 +5. 状态管理 +6. 数据传输对象 + +注意事项: +1. 结构体是值类型,大结构体传参考虑性能 +2. 嵌入字段的方法可能被重写 +3. 结构体比较要求所有字段可比较 +4. 注意内存对齐对结构体大小的影响 +5. 合理使用标签,避免过度复杂化 + +内存优化: +1. 字段排序:大字段在前,小字段在后 +2. 使用指针:大结构体考虑使用指针 +3. 空结构体:零内存占用,适合信号传递 +4. 字段对齐:了解内存对齐规则 +*/ \ No newline at end of file diff --git a/golang-learning/04-data-structures/05-pointers.go b/golang-learning/04-data-structures/05-pointers.go new file mode 100644 index 0000000..bbfd5fd --- /dev/null +++ b/golang-learning/04-data-structures/05-pointers.go @@ -0,0 +1,987 @@ +/* +05-pointers.go - Go 语言指针详解 + +学习目标: +1. 理解指针的概念和作用 +2. 掌握指针的声明和使用 +3. 学会指针的运算和操作 +4. 了解指针与函数参数的关系 +5. 掌握指针的实际应用场景 + +知识点: +- 指针的定义和特性 +- 指针的声明和初始化 +- 取地址操作符 & +- 解引用操作符 * +- 指针与函数参数 +- 指针与结构体 +- 指针的零值 nil +- 指针的安全使用 +*/ + +package main + +import ( + "fmt" +) + +func main() { + fmt.Println("=== Go 语言指针详解 ===\n") + + // 演示指针的基本概念 + demonstrateBasicPointers() + + // 演示指针的操作 + demonstratePointerOperations() + + // 演示指针与函数 + demonstratePointersWithFunctions() + + // 演示指针与结构体 + demonstratePointersWithStructs() + + // 演示指针与数组/切片 + demonstratePointersWithArraysSlices() + + // 演示指针的高级用法 + demonstrateAdvancedPointerUsage() + + // 演示指针的实际应用 + demonstratePracticalApplications() +} + +// demonstrateBasicPointers 演示指针的基本概念 +func demonstrateBasicPointers() { + fmt.Println("1. 指针的基本概念:") + + // 指针的基本特性 + fmt.Printf(" 指针的基本特性:\n") + fmt.Printf(" - 存储变量的内存地址\n") + fmt.Printf(" - 通过地址间接访问变量\n") + fmt.Printf(" - 零值是 nil\n") + fmt.Printf(" - 类型安全,不支持指针运算\n") + fmt.Printf(" - 自动垃圾回收管理\n") + + // 基本指针示例 + fmt.Printf(" 基本指针示例:\n") + + // 声明变量 + var x int = 42 + fmt.Printf(" 变量 x: %d\n", x) + fmt.Printf(" x 的地址: %p\n", &x) + + // 声明指针 + var p *int + fmt.Printf(" 指针 p 的零值: %v\n", p) + fmt.Printf(" p == nil: %t\n", p == nil) + + // 指针赋值 + p = &x + fmt.Printf(" p = &x 后:\n") + fmt.Printf(" p 的值(地址): %p\n", p) + fmt.Printf(" p 指向的值: %d\n", *p) + fmt.Printf(" p 的地址: %p\n", &p) + + // 通过指针修改值 + *p = 100 + fmt.Printf(" *p = 100 后:\n") + fmt.Printf(" x 的值: %d\n", x) + fmt.Printf(" *p 的值: %d\n", *p) + + // 指针的类型 + fmt.Printf(" 指针的类型:\n") + var intPtr *int + var floatPtr *float64 + var stringPtr *string + + fmt.Printf(" *int 指针: %T\n", intPtr) + fmt.Printf(" *float64 指针: %T\n", floatPtr) + fmt.Printf(" *string 指针: %T\n", stringPtr) + + fmt.Println() +} + +// demonstratePointerOperations 演示指针的操作 +func demonstratePointerOperations() { + fmt.Println("2. 指针的操作:") + + // 取地址操作符 & + fmt.Printf(" 取地址操作符 &:\n") + + a := 10 + b := 20 + c := 30 + + fmt.Printf(" 变量地址:\n") + fmt.Printf(" &a: %p\n", &a) + fmt.Printf(" &b: %p\n", &b) + fmt.Printf(" &c: %p\n", &c) + + // 解引用操作符 * + fmt.Printf(" 解引用操作符 *:\n") + + ptr := &a + fmt.Printf(" ptr 指向 a: %p\n", ptr) + fmt.Printf(" *ptr: %d\n", *ptr) + + // 修改指针指向 + ptr = &b + fmt.Printf(" ptr 指向 b: %p\n", ptr) + fmt.Printf(" *ptr: %d\n", *ptr) + + // 指针赋值 + fmt.Printf(" 指针赋值:\n") + + var p1, p2 *int + p1 = &a + p2 = p1 // 指针赋值,两个指针指向同一地址 + + fmt.Printf(" p1: %p, *p1: %d\n", p1, *p1) + fmt.Printf(" p2: %p, *p2: %d\n", p2, *p2) + fmt.Printf(" p1 == p2: %t\n", p1 == p2) + + // 通过不同指针修改同一变量 + *p1 = 99 + fmt.Printf(" *p1 = 99 后:\n") + fmt.Printf(" a: %d\n", a) + fmt.Printf(" *p2: %d\n", *p2) + + // nil 指针检查 + fmt.Printf(" nil 指针检查:\n") + + var nilPtr *int + fmt.Printf(" nilPtr: %v\n", nilPtr) + fmt.Printf(" nilPtr == nil: %t\n", nilPtr == nil) + + // 安全的指针使用 + if nilPtr != nil { + fmt.Printf(" *nilPtr: %d\n", *nilPtr) + } else { + fmt.Printf(" nilPtr 是 nil,不能解引用\n") + } + + // 指针比较 + fmt.Printf(" 指针比较:\n") + + x, y := 1, 2 + px, py := &x, &y + px2 := &x + + fmt.Printf(" px == py: %t (指向不同变量)\n", px == py) + fmt.Printf(" px == px2: %t (指向同一变量)\n", px == px2) + fmt.Printf(" px == nil: %t\n", px == nil) + + fmt.Println() +} + +// demonstratePointersWithFunctions 演示指针与函数 +func demonstratePointersWithFunctions() { + fmt.Println("3. 指针与函数:") + + // 值传递 vs 指针传递 + fmt.Printf(" 值传递 vs 指针传递:\n") + + x := 10 + fmt.Printf(" 原始值: %d\n", x) + + // 值传递 + modifyByValue(x) + fmt.Printf(" 值传递后: %d (未改变)\n", x) + + // 指针传递 + modifyByPointer(&x) + fmt.Printf(" 指针传递后: %d (已改变)\n", x) + + // 函数返回指针 + fmt.Printf(" 函数返回指针:\n") + + ptr := createInt(42) + fmt.Printf(" 返回的指针: %p\n", ptr) + fmt.Printf(" 指针指向的值: %d\n", *ptr) + + // 多个返回值包含指针 + value, valuePtr := createIntPair(100) + fmt.Printf(" 返回值: %d\n", value) + fmt.Printf(" 返回指针: %p, 值: %d\n", valuePtr, *valuePtr) + + // 指针作为函数参数的优势 + fmt.Printf(" 指针作为函数参数的优势:\n") + + // 交换变量 + a, b := 5, 10 + fmt.Printf(" 交换前: a=%d, b=%d\n", a, b) + swap(&a, &b) + fmt.Printf(" 交换后: a=%d, b=%d\n", a, b) + + // 避免大结构体拷贝 + fmt.Printf(" 避免大结构体拷贝:\n") + + person := Person{Name: "Alice", Age: 25, Email: "alice@example.com"} + fmt.Printf(" 修改前: %+v\n", person) + + // 使用指针避免结构体拷贝 + updatePersonByPointer(&person, 26) + fmt.Printf(" 指针修改后: %+v\n", person) + + fmt.Println() +} + +// demonstratePointersWithStructs 演示指针与结构体 +func demonstratePointersWithStructs() { + fmt.Println("4. 指针与结构体:") + + // 结构体指针的创建 + fmt.Printf(" 结构体指针的创建:\n") + + // 方式1: 先创建结构体,再取地址 + person1 := Person{Name: "Alice", Age: 25, Email: "alice@example.com"} + personPtr1 := &person1 + fmt.Printf(" 方式1: %+v\n", *personPtr1) + + // 方式2: 直接创建结构体指针 + personPtr2 := &Person{Name: "Bob", Age: 30, Email: "bob@example.com"} + fmt.Printf(" 方式2: %+v\n", *personPtr2) + + // 方式3: 使用 new 函数 + personPtr3 := new(Person) + personPtr3.Name = "Charlie" + personPtr3.Age = 35 + personPtr3.Email = "charlie@example.com" + fmt.Printf(" 方式3: %+v\n", *personPtr3) + + // 结构体指针的字段访问 + fmt.Printf(" 结构体指针的字段访问:\n") + + // 方式1: 显式解引用 + fmt.Printf(" (*personPtr1).Name: %s\n", (*personPtr1).Name) + + // 方式2: 自动解引用(推荐) + fmt.Printf(" personPtr1.Name: %s\n", personPtr1.Name) + + // 修改结构体字段 + fmt.Printf(" 修改结构体字段:\n") + fmt.Printf(" 修改前: %s\n", personPtr1.Name) + personPtr1.Name = "Alice Smith" + fmt.Printf(" 修改后: %s\n", personPtr1.Name) + fmt.Printf(" 原结构体: %s\n", person1.Name) // 也被修改了 + + // 结构体指针切片 + fmt.Printf(" 结构体指针切片:\n") + + people := []*Person{ + {Name: "David", Age: 28, Email: "david@example.com"}, + {Name: "Eve", Age: 32, Email: "eve@example.com"}, + {Name: "Frank", Age: 29, Email: "frank@example.com"}, + } + + fmt.Printf(" 人员列表:\n") + for i, person := range people { + fmt.Printf(" %d: %s (%d岁)\n", i+1, person.Name, person.Age) + } + + // 修改切片中的结构体 + people[0].Age = 29 + fmt.Printf(" 修改后第一个人的年龄: %d\n", people[0].Age) + + // 结构体指针映射 + fmt.Printf(" 结构体指针映射:\n") + + userMap := map[string]*Person{ + "alice": {Name: "Alice", Age: 25, Email: "alice@example.com"}, + "bob": {Name: "Bob", Age: 30, Email: "bob@example.com"}, + } + + if user, exists := userMap["alice"]; exists { + fmt.Printf(" 找到用户: %+v\n", *user) + user.Age = 26 // 修改映射中的结构体 + fmt.Printf(" 修改后: %+v\n", *user) + } + + fmt.Println() +} + +// demonstratePointersWithArraysSlices 演示指针与数组/切片 +func demonstratePointersWithArraysSlices() { + fmt.Println("5. 指针与数组/切片:") + + // 数组指针 + fmt.Printf(" 数组指针:\n") + + arr := [5]int{1, 2, 3, 4, 5} + arrPtr := &arr + + fmt.Printf(" 数组: %v\n", arr) + fmt.Printf(" 数组指针: %p\n", arrPtr) + fmt.Printf(" 通过指针访问: %v\n", *arrPtr) + + // 修改数组元素 + arrPtr[2] = 99 // 等同于 (*arrPtr)[2] = 99 + fmt.Printf(" 修改后数组: %v\n", arr) + + // 元素指针 + fmt.Printf(" 元素指针:\n") + + slice := []int{10, 20, 30, 40, 50} + fmt.Printf(" 切片: %v\n", slice) + + // 获取元素指针 + elemPtr := &slice[2] + fmt.Printf(" slice[2] 的地址: %p\n", elemPtr) + fmt.Printf(" slice[2] 的值: %d\n", *elemPtr) + + // 通过元素指针修改 + *elemPtr = 300 + fmt.Printf(" 修改后切片: %v\n", slice) + + // 指针切片 + fmt.Printf(" 指针切片:\n") + + var intPtrs []*int + for i := range slice { + intPtrs = append(intPtrs, &slice[i]) + } + + fmt.Printf(" 指针切片长度: %d\n", len(intPtrs)) + fmt.Printf(" 第一个指针指向的值: %d\n", *intPtrs[0]) + + // 通过指针切片修改原切片 + *intPtrs[1] = 200 + fmt.Printf(" 修改后原切片: %v\n", slice) + + // 数组指针 vs 指针数组 + fmt.Printf(" 数组指针 vs 指针数组:\n") + + // 数组指针:指向数组的指针 + var arrayPtr *[3]int = &[3]int{1, 2, 3} + fmt.Printf(" 数组指针类型: %T\n", arrayPtr) + fmt.Printf(" 数组指针值: %v\n", *arrayPtr) + + // 指针数组:元素是指针的数组 + x, y, z := 1, 2, 3 + var ptrArray [3]*int = [3]*int{&x, &y, &z} + fmt.Printf(" 指针数组类型: %T\n", ptrArray) + fmt.Printf(" 指针数组值: [%d, %d, %d]\n", *ptrArray[0], *ptrArray[1], *ptrArray[2]) + + fmt.Println() +}// demons +trateAdvancedPointerUsage 演示指针的高级用法 +func demonstrateAdvancedPointerUsage() { + fmt.Println("6. 指针的高级用法:") + + // 双重指针 + fmt.Printf(" 双重指针:\n") + + x := 42 + ptr := &x + ptrPtr := &ptr + + fmt.Printf(" x: %d\n", x) + fmt.Printf(" ptr: %p, *ptr: %d\n", ptr, *ptr) + fmt.Printf(" ptrPtr: %p, *ptrPtr: %p, **ptrPtr: %d\n", ptrPtr, *ptrPtr, **ptrPtr) + + // 通过双重指针修改 + **ptrPtr = 100 + fmt.Printf(" **ptrPtr = 100 后, x: %d\n", x) + + // 指针与接口 + fmt.Printf(" 指针与接口:\n") + + var shape Shape + + // 值类型实现接口 + circle := Circle{Radius: 5} + shape = circle + fmt.Printf(" 圆形面积: %.2f\n", shape.Area()) + + // 指针类型实现接口 + rect := &Rectangle{Width: 4, Height: 3} + shape = rect + fmt.Printf(" 矩形面积: %.2f\n", shape.Area()) + + // 指针与方法集 + fmt.Printf(" 指针与方法集:\n") + + counter := Counter{Value: 0} + fmt.Printf(" 初始计数器: %+v\n", counter) + + // 值类型调用指针接收者方法(自动取地址) + counter.Increment() + fmt.Printf(" 调用 Increment() 后: %+v\n", counter) + + // 指针类型调用值接收者方法(自动解引用) + counterPtr := &Counter{Value: 10} + value := counterPtr.GetValue() + fmt.Printf(" 指针调用值方法: %d\n", value) + + // unsafe 包的使用(谨慎使用) + fmt.Printf(" unsafe 包的使用:\n") + + var num int64 = 0x1234567890ABCDEF + fmt.Printf(" 原始值: 0x%X\n", num) + + // 获取指针 + numPtr := unsafe.Pointer(&num) + + // 将 int64 指针转换为 int32 指针(低32位) + lowPtr := (*int32)(numPtr) + fmt.Printf(" 低32位: 0x%X\n", *lowPtr) + + // 获取高32位 + highPtr := (*int32)(unsafe.Pointer(uintptr(numPtr) + 4)) + fmt.Printf(" 高32位: 0x%X\n", *highPtr) + + // 指针大小 + fmt.Printf(" 指针大小:\n") + fmt.Printf(" 指针大小: %d 字节\n", unsafe.Sizeof(ptr)) + fmt.Printf(" uintptr 大小: %d 字节\n", unsafe.Sizeof(uintptr(0))) + + fmt.Println() +} + +// demonstratePracticalApplications 演示指针的实际应用 +func demonstratePracticalApplications() { + fmt.Println("7. 指针的实际应用:") + + // 应用1: 链表实现 + fmt.Printf(" 应用1 - 链表实现:\n") + + list := &LinkedList{} + + // 添加节点 + list.Add(1) + list.Add(2) + list.Add(3) + + fmt.Printf(" 链表内容: ") + list.Print() + + // 查找节点 + if list.Contains(2) { + fmt.Printf(" 链表包含值 2\n") + } + + // 删除节点 + list.Remove(2) + fmt.Printf(" 删除 2 后: ") + list.Print() + + // 应用2: 树结构 + fmt.Printf(" 应用2 - 二叉树:\n") + + tree := &BinaryTree{} + + // 插入节点 + tree.Insert(5) + tree.Insert(3) + tree.Insert(7) + tree.Insert(1) + tree.Insert(9) + + fmt.Printf(" 中序遍历: ") + tree.InorderTraversal() + fmt.Printf("\n") + + // 查找节点 + if tree.Search(7) { + fmt.Printf(" 树中包含值 7\n") + } + + // 应用3: 缓存系统 + fmt.Printf(" 应用3 - LRU 缓存:\n") + + cache := NewLRUCache(3) + + // 添加缓存项 + cache.Put("a", 1) + cache.Put("b", 2) + cache.Put("c", 3) + + fmt.Printf(" 缓存状态: ") + cache.Print() + + // 访问缓存项 + if value, found := cache.Get("b"); found { + fmt.Printf(" 获取 'b': %d\n", value) + } + + // 添加新项(会淘汰最久未使用的) + cache.Put("d", 4) + fmt.Printf(" 添加 'd' 后: ") + cache.Print() + + // 应用4: 对象池 + fmt.Printf(" 应用4 - 对象池:\n") + + pool := NewObjectPool() + + // 获取对象 + obj1 := pool.Get() + obj2 := pool.Get() + + fmt.Printf(" 获取对象1: %p\n", obj1) + fmt.Printf(" 获取对象2: %p\n", obj2) + + // 使用对象 + obj1.Data = "Hello" + obj2.Data = "World" + + fmt.Printf(" 对象1数据: %s\n", obj1.Data) + fmt.Printf(" 对象2数据: %s\n", obj2.Data) + + // 归还对象 + pool.Put(obj1) + pool.Put(obj2) + + // 再次获取(可能复用之前的对象) + obj3 := pool.Get() + fmt.Printf(" 再次获取对象: %p (数据: %s)\n", obj3, obj3.Data) + + // 应用5: 回调函数 + fmt.Printf(" 应用5 - 回调函数:\n") + + processor := &DataProcessor{} + + // 设置回调函数 + processor.SetCallback(func(data string) { + fmt.Printf(" 处理数据: %s\n", data) + }) + + // 处理数据 + processor.Process("Hello, World!") + + // 应用6: 观察者模式 + fmt.Printf(" 应用6 - 观察者模式:\n") + + subject := &Subject{} + + // 添加观察者 + observer1 := &Observer{Name: "Observer1"} + observer2 := &Observer{Name: "Observer2"} + + subject.AddObserver(observer1) + subject.AddObserver(observer2) + + // 通知观察者 + subject.NotifyObservers("重要消息") + + fmt.Println() +} + +// ========== 类型定义 ========== + +// 基本结构体 +type Person struct { + Name string + Age int + Email string +} + +// 几何图形接口 +type Shape interface { + Area() float64 +} + +// 圆形 +type Circle struct { + Radius float64 +} + +func (c Circle) Area() float64 { + return 3.14159 * c.Radius * c.Radius +} + +// 矩形 +type Rectangle struct { + Width float64 + Height float64 +} + +func (r Rectangle) Area() float64 { + return r.Width * r.Height +} + +// 计数器 +type Counter struct { + Value int +} + +func (c *Counter) Increment() { + c.Value++ +} + +func (c Counter) GetValue() int { + return c.Value +} + +// 链表节点 +type ListNode struct { + Value int + Next *ListNode +} + +// 链表 +type LinkedList struct { + Head *ListNode +} + +func (l *LinkedList) Add(value int) { + newNode := &ListNode{Value: value} + if l.Head == nil { + l.Head = newNode + } else { + current := l.Head + for current.Next != nil { + current = current.Next + } + current.Next = newNode + } +} + +func (l *LinkedList) Contains(value int) bool { + current := l.Head + for current != nil { + if current.Value == value { + return true + } + current = current.Next + } + return false +} + +func (l *LinkedList) Remove(value int) { + if l.Head == nil { + return + } + + if l.Head.Value == value { + l.Head = l.Head.Next + return + } + + current := l.Head + for current.Next != nil { + if current.Next.Value == value { + current.Next = current.Next.Next + return + } + current = current.Next + } +} + +func (l *LinkedList) Print() { + current := l.Head + for current != nil { + fmt.Printf("%d ", current.Value) + current = current.Next + } + fmt.Printf("\n") +} + +// 二叉树节点 +type TreeNode struct { + Value int + Left *TreeNode + Right *TreeNode +} + +// 二叉树 +type BinaryTree struct { + Root *TreeNode +} + +func (t *BinaryTree) Insert(value int) { + t.Root = t.insertNode(t.Root, value) +} + +func (t *BinaryTree) insertNode(node *TreeNode, value int) *TreeNode { + if node == nil { + return &TreeNode{Value: value} + } + + if value < node.Value { + node.Left = t.insertNode(node.Left, value) + } else { + node.Right = t.insertNode(node.Right, value) + } + + return node +} + +func (t *BinaryTree) Search(value int) bool { + return t.searchNode(t.Root, value) +} + +func (t *BinaryTree) searchNode(node *TreeNode, value int) bool { + if node == nil { + return false + } + + if value == node.Value { + return true + } else if value < node.Value { + return t.searchNode(node.Left, value) + } else { + return t.searchNode(node.Right, value) + } +} + +func (t *BinaryTree) InorderTraversal() { + t.inorderNode(t.Root) +} + +func (t *BinaryTree) inorderNode(node *TreeNode) { + if node != nil { + t.inorderNode(node.Left) + fmt.Printf("%d ", node.Value) + t.inorderNode(node.Right) + } +} + +// LRU 缓存节点 +type CacheNode struct { + Key string + Value int + Prev *CacheNode + Next *CacheNode +} + +// LRU 缓存 +type LRUCache struct { + Capacity int + Cache map[string]*CacheNode + Head *CacheNode + Tail *CacheNode +} + +func NewLRUCache(capacity int) *LRUCache { + head := &CacheNode{} + tail := &CacheNode{} + head.Next = tail + tail.Prev = head + + return &LRUCache{ + Capacity: capacity, + Cache: make(map[string]*CacheNode), + Head: head, + Tail: tail, + } +} + +func (c *LRUCache) Get(key string) (int, bool) { + if node, exists := c.Cache[key]; exists { + c.moveToHead(node) + return node.Value, true + } + return 0, false +} + +func (c *LRUCache) Put(key string, value int) { + if node, exists := c.Cache[key]; exists { + node.Value = value + c.moveToHead(node) + } else { + newNode := &CacheNode{Key: key, Value: value} + c.Cache[key] = newNode + c.addToHead(newNode) + + if len(c.Cache) > c.Capacity { + tail := c.removeTail() + delete(c.Cache, tail.Key) + } + } +} + +func (c *LRUCache) addToHead(node *CacheNode) { + node.Prev = c.Head + node.Next = c.Head.Next + c.Head.Next.Prev = node + c.Head.Next = node +} + +func (c *LRUCache) removeNode(node *CacheNode) { + node.Prev.Next = node.Next + node.Next.Prev = node.Prev +} + +func (c *LRUCache) moveToHead(node *CacheNode) { + c.removeNode(node) + c.addToHead(node) +} + +func (c *LRUCache) removeTail() *CacheNode { + lastNode := c.Tail.Prev + c.removeNode(lastNode) + return lastNode +} + +func (c *LRUCache) Print() { + current := c.Head.Next + for current != c.Tail { + fmt.Printf("(%s:%d) ", current.Key, current.Value) + current = current.Next + } + fmt.Printf("\n") +} + +// 对象池 +type PoolObject struct { + Data string +} + +type ObjectPool struct { + pool []*PoolObject +} + +func NewObjectPool() *ObjectPool { + return &ObjectPool{ + pool: make([]*PoolObject, 0), + } +} + +func (p *ObjectPool) Get() *PoolObject { + if len(p.pool) > 0 { + obj := p.pool[len(p.pool)-1] + p.pool = p.pool[:len(p.pool)-1] + return obj + } + return &PoolObject{} +} + +func (p *ObjectPool) Put(obj *PoolObject) { + obj.Data = "" // 重置对象 + p.pool = append(p.pool, obj) +} + +// 数据处理器 +type DataProcessor struct { + callback func(string) +} + +func (d *DataProcessor) SetCallback(callback func(string)) { + d.callback = callback +} + +func (d *DataProcessor) Process(data string) { + if d.callback != nil { + d.callback(data) + } +} + +// 观察者模式 +type Observer struct { + Name string +} + +func (o *Observer) Update(message string) { + fmt.Printf(" %s 收到消息: %s\n", o.Name, message) +} + +type Subject struct { + observers []*Observer +} + +func (s *Subject) AddObserver(observer *Observer) { + s.observers = append(s.observers, observer) +} + +func (s *Subject) NotifyObservers(message string) { + for _, observer := range s.observers { + observer.Update(message) + } +} + +// ========== 辅助函数 ========== + +// 值传递函数 +func modifyByValue(x int) { + x = 999 + fmt.Printf(" 函数内部: %d\n", x) +} + +// 指针传递函数 +func modifyByPointer(x *int) { + *x = 888 + fmt.Printf(" 函数内部: %d\n", *x) +} + +// 返回指针的函数 +func createInt(value int) *int { + x := value // 局部变量 + return &x // 返回局部变量的地址(Go 会自动分配到堆上) +} + +// 返回值和指针 +func createIntPair(value int) (int, *int) { + x := value + return x, &x +} + +// 交换函数 +func swap(a, b *int) { + *a, *b = *b, *a +} + +// 更新结构体 +func updatePersonByPointer(p *Person, newAge int) { + p.Age = newAge +} + +/* +运行这个程序: +go run 05-pointers.go + +学习要点: +1. 指针存储变量的内存地址,通过地址间接访问变量 +2. 使用 & 获取地址,使用 * 解引用指针 +3. 指针的零值是 nil,使用前要检查 +4. 指针传参可以避免大对象拷贝,允许函数修改原变量 +5. Go 的指针是类型安全的,不支持指针运算 + +指针的特性: +1. 类型安全:不同类型的指针不能相互赋值 +2. 自动管理:垃圾回收器自动管理内存 +3. 无指针运算:不支持 C 风格的指针运算 +4. 自动解引用:结构体指针可以直接访问字段 +5. 方法调用:自动在值和指针间转换 + +指针的用途: +1. 避免大对象拷贝 +2. 允许函数修改参数 +3. 实现数据结构(链表、树等) +4. 共享数据 +5. 可选值表示 + +指针与函数: +1. 值传递:传递变量的拷贝 +2. 指针传递:传递变量的地址 +3. 返回指针:可以返回局部变量的地址 +4. 方法接收者:值接收者 vs 指针接收者 + +最佳实践: +1. 使用前检查指针是否为 nil +2. 大结构体使用指针传递 +3. 需要修改参数时使用指针 +4. 合理使用指针避免内存泄漏 +5. 优先使用值类型,必要时使用指针 + +常见应用场景: +1. 数据结构实现 +2. 对象池和缓存 +3. 回调函数 +4. 观察者模式 +5. 链式数据结构 +6. 可选参数 + +注意事项: +1. nil 指针解引用会 panic +2. 指针比较比较的是地址 +3. 指针可能导致内存泄漏 +4. 避免返回栈变量的指针(Go 会自动处理) +5. 理解值接收者和指针接收者的区别 + +unsafe 包: +1. 提供底层内存操作 +2. 绕过类型安全检查 +3. 谨慎使用,可能破坏内存安全 +4. 主要用于系统编程和性能优化 +*/ \ No newline at end of file