【GoLang#3】:数据结构(切片 | map 映射)

发布于:2025-07-30 ⋅ 阅读:(17) ⋅ 点赞:(0)

一、切片

之前我们在数组那举过一个例子:关于 值类型 和 引用类型的,如下:

func main(){
    // 值类型: 数组
	var a1 = [...]int{1, 2, 3}
	a2 := a1
	a1[0] = 11
	fmt.Println(a1) // 11 2 3
	fmt.Println(a2) // 1 2 3

	// 引用类型: 切片
	var b1 = []int{1, 2, 3}
	b2 := b1
	b1[0] = 11
	fmt.Println(b1) // 11 2 3
	fmt.Println(b2) // 11 2 3
}

现在我们来正式了解一下切片是什么吧

定义:切片(slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。切片是一个 引用类型,它的内部结构包含 地址长度容量

type slice struct {
    array unsafe.Pointer  // 指向底层数组的指针
    len   int             // 切片长度
    cap   int             // 切片容量
}

声明切片类型的基本语法:var name []T

  1. name:表示变量名
  2. T:表示切片中的元素类型

如下

package main
import "fmt"

func main() {
    // 切片和数组的区别
    var arr [5]int           // 数组:固定长度
    var slice []int          // 切片:动态长度
    
    fmt.Printf("数组: %v, 长度: %d\n", arr, len(arr))
    fmt.Printf("切片: %v, 长度: %d\n", slice, len(slice))
}

1. 切片的创建方式

下面是多种创建切片的方法:

func main() {
    // 1. 从数组创建切片
    arr := [5]int{1, 2, 3, 4, 5}
    slice1 := arr[1:4]        // [2 3 4] - 从索引1到3
    slice2 := arr[:]          // [1 2 3 4 5] - 整个数组
    slice3 := arr[:3]         // [1 2 3] - 从开始到索引2
    slice4 := arr[2:]         // [3 4 5] - 从索引2到结束
    
    fmt.Println("slice1:", slice1)
    fmt.Println("slice2:", slice2)
    fmt.Println("slice3:", slice3)
    fmt.Println("slice4:", slice4)
    
    // 2. 使用 make 创建切片
    slice5 := make([]int, 5)        // 长度5,容量5,元素为0
    slice6 := make([]int, 3, 10)    // 长度3,容量10
    
    fmt.Printf("slice5: %v, 长度: %d, 容量: %d\n", slice5, len(slice5), cap(slice5))
    fmt.Printf("slice6: %v, 长度: %d, 容量: %d\n", slice6, len(slice6), cap(slice6))
    
    // 3. 字面量创建切片
    slice7 := []int{1, 2, 3, 4, 5}
    slice8 := []string{"hello", "world"}
    
    fmt.Println("slice7:", slice7)
    fmt.Println("slice8:", slice8)
    
    // 4. 使用 new 创建(不常用)
    slice9 := *new([]int)  // 创建空切片
    fmt.Printf("slice9: %v, 长度: %d, 容量: %d\n", slice9, len(slice9), cap(slice9)) // [] 0 0
}

理解:这里的数组创建切片 [] 的意思

语法格式array[low : high : max]

其中:

  • low :起始索引(包含)
  • high :结束索引(不包含
  • max :可选参数,指定切片的容量上限

而实际的切片长度和容量计算为:(在2.2 切片长度和容量那里有举例说明)

  1. 长度high - low
  2. 容量max - low(如果不指定 max,则容量到数组末尾)

2. 切片的基本操作

2.1 访问和修改元素
func main() {
    slice := []int{10, 20, 30, 40, 50}
    
    // 访问元素
    fmt.Println("第一个元素:", slice[0])
    fmt.Println("最后一个元素:", slice[len(slice)-1])
    
    // 修改元素
    slice[0] = 100
    fmt.Println("修改后:", slice)
    
    // 遍历切片
    fmt.Println("正向遍历:")
    for i := 0; i < len(slice); i++ {
        fmt.Printf("slice[%d] = %d ", i, slice[i])
    }
    fmt.Println()
    
    fmt.Println("range 遍历:")
    for index, value := range slice {
        fmt.Printf("索引 %d: 值 %d ", index, value)
    }
    fmt.Println()
    
    // 只遍历索引
    for index := range slice {
        fmt.Printf("索引: %d ", index)
    }
    fmt.Println()
    
    // 只遍历值
    for _, value := range slice {
        fmt.Printf("值: %d ", value)
    }
}
2.2 切片长度和容量
func printSliceInfo(name string, s []int) {
    fmt.Printf("%s: %v, 长度: %d, 容量: %d\n", name, s, len(s), cap(s))
}

func main() {
    arr := [6]int{1, 2, 3, 4, 5, 6}
    
    // 不同切片的长度和容量
    s1 := arr[1:4]   // [2 3 4], 
    s2 := arr[2:5]   // [3 4 5]
    s3 := arr[1:4:5] // [2 3 4],指定容量为5-1=4
    
    printSliceInfo("s1", s1)
    printSliceInfo("s2", s2)
    printSliceInfo("s3", s3)
    
    // make 创建的切片
    s4 := make([]int, 3, 10)  // 长度3,容量10
    printSliceInfo("s4", s4)
}

注意

  • 切片的长度表示切片中当前包含的元素个数,可以通过 len(slice) 函数获取;
  • 而切片的容量是指从切片的第一个元素开始,到其底层数组末尾的元素个数,可以通过 cap(slice)函数获取。

简单来说,长度是实际元素的数量,容量是切片在不分配新内存的情况下,最多能追加的元素数量。

2.3 切片扩容操作

① append 操作和扩容

func main() {
	// 初始切片
	slice := make([]int, 0, 2) // 长度0,容量2
	fmt.Printf("初始时: ")
	printSliceInfo(slice)

	// 逐步添加元素观察扩容
	for i := 1; i <= 5; i++ {
		slice = append(slice, i)
		fmt.Printf("添加 %d 后: ", i)
		printSliceInfo(slice)
	}
}

func printSliceInfo(s []int) {
	fmt.Printf("长度: %d, 容量: %d, 内容: %v\n", len(s), cap(s), s)
}

结果如下

初始时: 长度: 0, 容量: 2, 内容: []
添加 1 后: 长度: 1, 容量: 2, 内容: [1]
添加 2 后: 长度: 2, 容量: 2, 内容: [1 2]
添加 3 后: 长度: 3, 容量: 4, 内容: [1 2 3]
添加 4 后: 长度: 4, 容量: 4, 内容: [1 2 3 4]
添加 5 后: 长度: 5, 容量: 8, 内容: [1 2 3 4 5]

② 切片扩容规则

func main() {
    // 演示扩容机制
    slice := make([]int, 0, 1)
    addresses := make([]uintptr, 0)
    
    for i := 0; i < 10; i++ {
        // 记录底层数组地址
        if len(slice) > 0 {
            addresses = append(addresses, getSliceAddress(slice))
        }
        slice = append(slice, i)
    }
    
    // 检查地址变化(表示重新分配了底层数组)
    fmt.Println("地址变化点:")
    for i := 1; i < len(addresses); i++ {
        if addresses[i] != addresses[i-1] {
            fmt.Printf("在添加第 %d 个元素时发生扩容\n", i)
        }
    }
}

// 获取切片底层数组地址的辅助函数
func getSliceAddress(s []int) uintptr {
    if len(s) == 0 {
        return 0
    }
    return uintptr(unsafe.Pointer(&s[0]))
}

// 输出如下:
地址变化点:
在添加第 1 个元素时发生扩容
在添加第 2 个元素时发生扩容
在添加第 4 个元素时发生扩容
在添加第 8 个元素时发生扩容

3. 切片的高级操作

3.1 切片复制和追加
func main() {
    // 1. append 操作
    slice1 := []int{1, 2, 3}
    slice1 = append(slice1, 4, 5)  // 添加多个元素
    fmt.Println("append 多个元素:", slice1)	// 1 2 3 4 5
    
    // 追加另一个切片
    slice2 := []int{6, 7, 8}
    slice1 = append(slice1, slice2...)  // 使用 ... 展开切片
    fmt.Println("append 切片:", slice1) // 1 2 3 4 5 6 7 8
    
    // 2. copy 操作
    src := []int{10, 20, 30, 40, 50}
    dst := make([]int, 3)
    n := copy(dst, src)  // 从 src 复制到 dst
    fmt.Printf("复制了 %d 个元素: %v\n", n, dst)
    
    // 复制到更大切片
    dst2 := make([]int, 10)
    n2 := copy(dst2, src)
    fmt.Printf("复制了 %d 个元素: %v\n", n2, dst2)
    
    // 3. 切片截取
    original := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    sub1 := original[2:5]    // [3 4 5]
    sub2 := original[:4]     // [1 2 3 4]
    sub3 := original[6:]     // [7 8 9 10]
    
    fmt.Println("原切片:", original)
    fmt.Println("sub1:", sub1)
    fmt.Println("sub2:", sub2)
    fmt.Println("sub3:", sub3)
}
3.2 切片删除操作
func main() {
    // 1. 删除指定索引的元素
    slice := []int{1, 2, 3, 4, 5}
    fmt.Println("原切片:", slice)
    
    // 删除索引为2的元素(值为3)
    index := 2
    slice = append(slice[:index], slice[index+1:]...)
    fmt.Println("删除索引2后:", slice)
    
    // 2. 删除第一个元素
    slice = slice[1:]
    fmt.Println("删除第一个元素后:", slice)
    
    // 3. 删除最后一个元素
    slice = slice[:len(slice)-1]
    fmt.Println("删除最后一个元素后:", slice)
    
    // 4. 按值删除(删除所有匹配的值)
    slice2 := []int{1, 2, 3, 2, 4, 2, 5}
    slice2 = removeValue(slice2, 2)
    fmt.Println("删除所有值为2的元素:", slice2)
}

// 删除指定值的所有元素
func removeValue(slice []int, value int) []int {
    result := make([]int, 0)
    for _, v := range slice {
        if v != value {
            result = append(result, v)
        }
    }
    return result
}

// 更高效的删除方式(保持原切片顺序)
func removeAt(slice []int, index int) []int {
    if index < 0 || index >= len(slice) {
        return slice
    }
    return append(slice[:index], slice[index+1:]...)
}

4. 切片注意事项

① 共享底层数组的问题

func main() {
	// 切片共享底层数组的陷阱
	original := []int{1, 2, 3, 4, 5}
	slice1 := original[1:4] // [2 3 4]

	fmt.Println("原始切片:", original)
	fmt.Println("slice1:", slice1) // [2, 3, 4]

	// 修改 slice1 会影响 original
	slice1[0] = 100
	fmt.Printf("修改 slice1[0] = 100 后, ")
	fmt.Printf("原始切片: %d, ", original) // [1 100 3 4 5]
	fmt.Println("slice1: ", slice1)    // [100 3 4]

	// 正确的复制方式
	slice2 := make([]int, len(original[1:4]))
	copy(slice2, original[1:4]) // [100 3 4]
	slice2[0] = 200
	fmt.Printf("正确复制后修改,")
	fmt.Printf("原始切片: %d,", original) // [1 100 3 4 5]
	fmt.Println("slice2:", slice2) // [200 3 4]
}

② 切片扩容陷阱

func main() {
    // 切片扩容陷阱
    s1 := []int{1, 2, 3}
    s2 := s1[1:]  // [2 3]
    
    fmt.Printf("s1: %v, 地址: %p\n", s1, &s1[0])
    fmt.Printf("s2: %v, 地址: %p\n", s2, &s2[0])
    
    // 当 s2 扩容时,可能会重新分配底层数组
    s2 = append(s2, 4, 5, 6, 7, 8, 9)  // 足够多的元素触发扩容
    
    fmt.Printf("扩容后 s1: %v\n", s1)
    fmt.Printf("扩容后 s2: %v\n", s2)
    fmt.Printf("扩容后 s2 地址: %p\n", &s2[0])
}

5. 应用场景

① 字符串处理

func main() {
    // 字符串转切片
    str := "hello,world,go"
    parts := strings.Split(str, ",")
    fmt.Println("分割结果:", parts)
    
    // 切片转字符串
    joined := strings.Join(parts, "-")
    fmt.Println("连接结果:", joined)
    
    // 字符串切片操作
    runes := []rune("Hello, 世界")
    fmt.Println("字符切片:", runes)
    fmt.Printf("第一个字符: %c\n", runes[0])
    fmt.Printf("最后一个字符: %c\n", runes[len(runes)-1])
}

② 切片排序

func main() {
    // 整数切片排序
    numbers := []int{5, 2, 8, 1, 9, 3}
    fmt.Println("排序前:", numbers)
    sort.Ints(numbers)
    fmt.Println("排序后:", numbers)
    
    // 字符串切片排序
    words := []string{"banana", "apple", "cherry", "date"}
    fmt.Println("排序前:", words)
    sort.Strings(words)
    fmt.Println("排序后:", words)
    
    // 自定义排序
    type Person struct {
        Name string
        Age  int
    }
    
    people := []Person{
        {"Alice", 30},
        {"Bob", 25},
        {"Charlie", 35},
    }
    
    // 按年龄排序
    sort.Slice(people, func(i, j int) bool {
        return people[i].Age < people[j].Age
    })
    
    fmt.Println("按年龄排序:")
    for _, p := range people {
        fmt.Printf("%s: %d岁\n", p.Name, p.Age)
    }
}

③ 切片搜索

func main() {
    // 有序切片的二分搜索
    sorted := []int{1, 3, 5, 7, 9, 11, 13, 15}
    
    // 使用 sort.SearchInts
    index := sort.SearchInts(sorted, 7)
    if index < len(sorted) && sorted[index] == 7 {
        fmt.Printf("找到 7,索引为: %d\n", index)
    } else {
        fmt.Println("未找到 7")
    }
    
    // 自定义搜索
    target := 6
    index2 := sort.Search(len(sorted), func(i int) bool {
        return sorted[i] >= target
    })
    
    if index2 < len(sorted) && sorted[index2] == target {
        fmt.Printf("找到 %d,索引为: %d\n", target, index2)
    } else {
        fmt.Printf("%d 应该插入到索引: %d\n", target, index2)
    }
}

6. 性能优化技巧

① 预分配容量

func main() {
    // 不预分配容量
    start := time.Now()
    var slice1 []int
    for i := 0; i < 100000; i++ {
        slice1 = append(slice1, i)
    }
    duration1 := time.Since(start)
    
    // 预分配容量
    start = time.Now()
    slice2 := make([]int, 0, 100000)  // 预分配足够容量
    for i := 0; i < 100000; i++ {
        slice2 = append(slice2, i)
    }
    duration2 := time.Since(start)
    
    fmt.Printf("不预分配耗时: %v\n", duration1)
    fmt.Printf("预分配耗时: %v\n", duration2)
    fmt.Printf("性能提升: %.2f 倍\n", float64(duration1)/float64(duration2))
}

② 避免不必要复制

func main() {
    // 大切片的处理
    bigSlice := make([]int, 1000000)
    for i := range bigSlice {
        bigSlice[i] = i
    }
    
    // 错误的方式:复制整个大切片
    smallSlice1 := bigSlice[999990:]  // 可能复制整个底层数组
    
    // 正确的方式:创建新的小切片
    smallSlice2 := make([]int, 10)
    copy(smallSlice2, bigSlice[999990:])
    
    fmt.Printf("smallSlice1 长度: %d, 容量: %d\n", len(smallSlice1), cap(smallSlice1))
    fmt.Printf("smallSlice2 长度: %d, 容量: %d\n", len(smallSlice2), cap(smallSlice2))
}

二、Map 映射

Map 是 Go 语言中的键值对(key-value)数据结构,也称为字典或哈希表。它提供了快速的键值查找能力。

map 语法结构

map[键类型]值类型

// 比如
map[string]string
map[string]int

1. Map 的基本操作

① 遍历 Map 的多种方式

func main() {
    // 1. 使用 make 创建空 map
    var map1 map[string]int
    map1 = make(map[string]int)
    
    // 或者简写
    map2 := make(map[string]int)
    
    // 2. 字面量创建 map
    map3 := map[string]int{
        "张三": 25,
        "李四": 30,
        "王五": 28,
    }
    
    // 3. 创建空的字面量 map
    map4 := map[string]int{}
    
    // 4. 指定初始容量(可选优化)
    map5 := make(map[string]int, 100)  // 预分配容量
    
    fmt.Printf("map1: %v\n", map1)
    fmt.Printf("map2: %v\n", map2)
    fmt.Printf("map3: %v\n", map3)
    fmt.Printf("map4: %v\n", map4)
    fmt.Printf("map5: %v\n", map5)
    
    // 不同类型的 map
    stringMap := map[string]string{
        "name":    "张三",
        "city":    "北京",
        "country": "中国",
    }
    
    intMap := map[int]string{
        1: "一",
        2: "二",
        3: "三",
    }
    
    fmt.Printf("stringMap: %v\n", stringMap)
    fmt.Printf("intMap: %v\n", intMap)
}

② 增、删、改、查操作

func main() {
    // 创建 map
    ages := make(map[string]int)
    
    // 1. 添加/修改元素(赋值操作)
    ages["张三"] = 25
    ages["李四"] = 30
    ages["王五"] = 28
    
    fmt.Println("添加元素后:", ages)
    
    // 2. 修改元素
    ages["张三"] = 26  // 修改张三的年龄
    fmt.Println("修改后:", ages)
    
    // 3. 查找元素
    age, exists := ages["李四"]
    if exists {
        fmt.Printf("李四的年龄: %d\n", age)
    } else {
        fmt.Println("未找到李四")
    }
    
    // 简化的查找方式(如果不需要区分零值和不存在)
    fmt.Printf("王五的年龄: %d\n", ages["王五"])
    
    // 4. 删除元素
    delete(ages, "王五")
    fmt.Println("删除王五后:", ages)
    
    // 删除不存在的键不会报错
    delete(ages, "不存在的人")
    fmt.Println("删除不存在的键后:", ages)
}

③ Map 的遍历

func main() {
    scores := map[string]float64{
        "数学": 95.5,
        "英语": 87.0,
        "语文": 92.5,
        "物理": 88.0,
    }
    
    fmt.Println("原始 map:", scores)
    fmt.Println()
    
    // 1. 基本遍历(注意:顺序不固定)
    fmt.Println("基本遍历:")
    for subject, score := range scores {
        fmt.Printf("%s: %.1f分\n", subject, score)
    }
    fmt.Println()
    
    // 2. 只遍历键
    fmt.Println("只遍历键:")
    for subject := range scores {
        fmt.Printf("科目: %s\n", subject)
    }
    fmt.Println()
    
    // 3. 只遍历值
    fmt.Println("只遍历值:")
    for _, score := range scores {
        fmt.Printf("分数: %.1f\n", score)
    }
    fmt.Println()
    
    // 4. 按照特定顺序遍历(需要先排序键)
    fmt.Println("按字母顺序遍历:")
    keys := make([]string, 0, len(scores))
    for key := range scores {
        keys = append(keys, key)
    }
    
    // 对键进行排序
    // 注意:需要导入 sort 包
    fmt.Println("keys:", keys)
}

2. Map 特性

① Map 的零值和 nil

func main() {
	// 1. nil map
	var nilMap map[string]int
	fmt.Printf("nilMap: %v, 是否为 nil: %t\n", nilMap, nilMap == nil) // map[] true

	// nil map 可以读取(返回零值)
	fmt.Printf("nilMap['key']: %d\n", nilMap["key"]) // 0

	// nil map 不能写入(会导致 panic)
	// nilMap["key"] = 1  // 这会 panic!

	// 2. 空 map
	emptyMap := make(map[string]int)
	fmt.Printf("emptyMap: %v, 是否为 nil: %t\n", emptyMap, emptyMap == nil) // map[] false

	// 空 map 可以正常读写
	emptyMap["key"] = 1
	fmt.Printf("emptyMap['key']: %d\n", emptyMap["key"]) 

	// 3. 检查键是否存在
	value, exists := emptyMap["不存在的键"] 
	if !exists {
		fmt.Println("键不存在,返回零值:", value)
	}
}

② Map 的键类型限制

func main() {
    // Map 的键必须是可比较的类型
    
    // ✅ 合法的键类型
    intMap := map[int]string{1: "一", 2: "二"}
    stringMap := map[string]int{"one": 1, "two": 2}
    boolMap := map[bool]string{true: "真", false: "假"}
    
    // 复合键类型
    pointMap := map[[2]int]string{[2]int{0, 0}: "原点"}
    
    fmt.Println("intMap:", intMap)
    fmt.Println("stringMap:", stringMap)
    fmt.Println("boolMap:", boolMap)
    fmt.Println("pointMap:", pointMap)
    
    // ❌ 非法的键类型(编译错误)
    // sliceMap := map[[]int]string{}     // 错误:slice 不可比较
    // mapMap := map[map[string]int]string{} // 错误:map 不可比较
    // funcMap := map[func()]string{}     // 错误:函数不可比较
}

3. Map 嵌套和高级操作

嵌套 Map 和复杂数据结构

func main() {
    // 1. 嵌套 Map
    studentGrades := map[string]map[string]float64{
        "张三": {
            "数学": 95.5,
            "英语": 87.0,
            "语文": 92.5,
        },
        "李四": {
            "数学": 88.0,
            "英语": 91.5,
            "语文": 89.0,
        },
    }
    
    fmt.Println("嵌套 Map:")
    for student, grades := range studentGrades {
        fmt.Printf("%s 的成绩:\n", student)
        for subject, score := range grades {
            fmt.Printf("  %s: %.1f\n", subject, score)
        }
    }
    
    // 访问嵌套 Map
    zhangsanMath := studentGrades["张三"]["数学"]
    fmt.Printf("\n张三的数学成绩: %.1f\n", zhangsanMath)
    
    // 2. Map + Slice 组合
    classStudents := map[string][]string{
        "一班": {"张三", "李四", "王五"},
        "二班": {"赵六", "钱七", "孙八"},
    }
    
    fmt.Println("\n班级学生:")
    for class, students := range classStudents {
        fmt.Printf("%s: %v\n", class, students)
    }
}

Map 函数

// 统计字符出现次数
func countChars(s string) map[rune]int {
    counts := make(map[rune]int)
    for _, char := range s {
        counts[char]++
    }
    return counts
}

// 查找最大值对应的键
func findMaxKey(m map[string]int) (string, int) {
    if len(m) == 0 {
        return "", 0
    }
    
    maxKey := ""
    maxValue := 0
    for key, value := range m {
        if value > maxValue {
            maxValue = value
            maxKey = key
        }
    }
    return maxKey, maxValue
}

func main() {
    // 字符统计示例
    text := "hello world"
    charCounts := countChars(text)
    fmt.Printf("'%s' 中字符统计: %v\n", text, charCounts)
    
    // 查找最大值
    scores := map[string]int{
        "张三": 95,
        "李四": 87,
        "王五": 92,
        "赵六": 89,
    }
    
    bestStudent, highestScore := findMaxKey(scores)
    fmt.Printf("最高分: %s 得分 %d\n", bestStudent, highestScore)
}

4. 并发安全

① Map 不是并发安全

func main() {
    regularMap := make(map[int]int) // 普通 map 在并发访问时会有问题
    var wg sync.WaitGroup // 并发读写会导致 panic
    
    // 启动多个 goroutine 同时写入
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            for j := 0; j < 100; j++ {
                regularMap[id*100+j] = id
            }
        }(i)
    }
    
    // 启动多个 goroutine 同时读取
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for j := 0; j < 100; j++ {
                _ = regularMap[j]
            }
        }()
    }
    
    wg.Wait()
    fmt.Println("普通 map 并发操作完成(可能 panic)")
}

② sync.Map 并发安全的 Map

func main() {
    // sync.Map 是并发安全的
    var safeMap sync.Map
    
    // 存储值
    safeMap.Store("key1", "value1")
    safeMap.Store("key2", "value2")
    
    // 读取值
    if value, ok := safeMap.Load("key1"); ok {
        fmt.Println("key1:", value)
    }
    
    // 删除值
    safeMap.Delete("key1")
    
    // 遍历
    fmt.Println("遍历 sync.Map:")
    safeMap.Range(func(key, value interface{}) bool {
        fmt.Printf("%v: %v\n", key, value)
        return true  // 返回 true 继续遍历,false 停止
    })
    
    // 原子操作
    if actual, loaded := safeMap.LoadOrStore("key3", "value3"); loaded {
        fmt.Println("key3 已存在,值为:", actual)
    } else {
        fmt.Println("key3 不存在,已存储新值")
    }
}

5. 性能优化

① 预分配容量优化

func main() {
    // 测试预分配容量的性能差异
    const size = 100000
    
    // 不预分配容量
    start := time.Now()
    map1 := make(map[int]int)
    for i := 0; i < size; i++ {
        map1[i] = i * 2
    }
    duration1 := time.Since(start)
    
    // 预分配容量
    start = time.Now()
    map2 := make(map[int]int, size)  // 预分配容量
    for i := 0; i < size; i++ {
        map2[i] = i * 2
    }
    duration2 := time.Since(start)
    
    fmt.Printf("不预分配耗时: %v\n", duration1)
    fmt.Printf("预分配耗时: %v\n", duration2)
    fmt.Printf("性能提升: %.2f%%\n", 
        float64(duration1-duration2)/float64(duration1)*100)
}

② 内存使用优化

func main() {
    // 1. 及时删除不需要的键值对
    cache := make(map[string]string)
    
    // 模拟缓存使用
    for i := 0; i < 1000; i++ {
        key := fmt.Sprintf("key_%d", i)
        cache[key] = fmt.Sprintf("value_%d", i)
    }
    
    fmt.Printf("缓存大小: %d\n", len(cache))
    
    // 清理旧缓存
    for key := range cache {
        if len(key) > 10 {
            delete(cache, key)
        }
    }
    
    fmt.Printf("清理后大小: %d\n", len(cache))
    
    // 2. 重置 map(完全清空)
    // 方法1:重新创建
    cache = make(map[string]string)
    
    // 方法2:逐个删除(不推荐)
    // for key := range cache {
    //     delete(cache, key)
    // }
}

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到