This commit is contained in:
2025-08-24 01:15:18 +08:00
parent e51feb1296
commit ec23aa5c26
6 changed files with 3631 additions and 3 deletions

View File

@@ -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演示指针的概念和操作
- 提供不同数据结构的使用场景建议

View File

@@ -195,5 +195,360 @@ func demonstrateArrayOperations() {
fmt.Printf(" 未找到 '%s'\n", target)
}
fmt.Println()
}// 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. 多维数组的内存布局是行优先的
*/

View File

@@ -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. 大切片的内存泄漏问题
*/

View File

@@ -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不是空映射
*/

View File

@@ -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. 字段对齐:了解内存对齐规则
*/

View File

@@ -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. 主要用于系统编程和性能优化
*/