继续
This commit is contained in:
@@ -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,演示指针的概念和操作
|
||||
- 提供不同数据结构的使用场景建议
|
||||
|
@@ -196,4 +196,359 @@ func demonstrateArrayOperations() {
|
||||
}
|
||||
|
||||
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. 多维数组的内存布局是行优先的
|
||||
*/
|
672
golang-learning/04-data-structures/02-slices.go
Normal file
672
golang-learning/04-data-structures/02-slices.go
Normal 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. 大切片的内存泄漏问题
|
||||
*/
|
722
golang-learning/04-data-structures/03-maps.go
Normal file
722
golang-learning/04-data-structures/03-maps.go
Normal 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,不是空映射
|
||||
*/
|
892
golang-learning/04-data-structures/04-structs.go
Normal file
892
golang-learning/04-data-structures/04-structs.go
Normal 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. 字段对齐:了解内存对齐规则
|
||||
*/
|
987
golang-learning/04-data-structures/05-pointers.go
Normal file
987
golang-learning/04-data-structures/05-pointers.go
Normal 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. 主要用于系统编程和性能优化
|
||||
*/
|
Reference in New Issue
Block a user