Go学习第7天:Map集合 + 递归函数 + 类型转换

Go 语言:Map集合、递归函数、类型转换

  • [一、Map 集合](#一、Map 集合)
    • [1.1 核心特性](#1.1 核心特性)
    • [1.2 Map 创建方式](#1.2 Map 创建方式)
      • [方式1:make 函数创建(工程首选)](#方式1:make 函数创建(工程首选))
      • 方式2:字面量直接初始化
      • [方式3:仅声明(nil Map)](#方式3:仅声明(nil Map))
    • [1.3 Map 基础操作](#1.3 Map 基础操作)
      • [1. 增 / 改 元素](#1. 增 / 改 元素)
      • [2. 查询元素](#2. 查询元素)
      • [3. 获取长度](#3. 获取长度)
      • [4. 遍历 Map](#4. 遍历 Map)
      • [5. 删除元素](#5. 删除元素)
    • [1.4 Map 高频踩坑](#1.4 Map 高频踩坑)
  • 二、递归函数
    • [2.1 递归两大必要条件](#2.1 递归两大必要条件)
    • [2.2 基础语法](#2.2 基础语法)
    • [2.3 经典递归示例](#2.3 经典递归示例)
    • [2.4 递归优缺点 & 对比迭代](#2.4 递归优缺点 & 对比迭代)
    • [2.5 递归高频踩坑](#2.5 递归高频踩坑)
  • 三、类型转换
    • [3.1 基础数值类型转换](#3.1 基础数值类型转换)
    • [3.2 字符串与基本类型互转](#3.2 字符串与基本类型互转)
      • [1. 字符串 ↔ 整型](#1. 字符串 ↔ 整型)
      • [2. 字符串 ↔ 浮点型](#2. 字符串 ↔ 浮点型)
      • 踩坑
    • [3.3 接口类型转换(类型断言)](#3.3 接口类型转换(类型断言))
    • [3.4 类型转换通用踩坑](#3.4 类型转换通用踩坑)
  • 四、知识点速记

延续前文学习体系,本文讲解 Map 集合、递归函数、类型转换 三大核心知识点,每部分包含 语法说明、完整示例、核心规则、高频踩坑 ,代码可直接在 VSCode + Go Modules 环境运行。

目录

  1. Map 集合
  2. 递归函数
  3. 类型转换
  4. 综合练习 & 知识点速记

一、Map 集合

Map 是 Go 语言无序键值对(key-value)集合 ,属于引用类型 ,底层采用哈希表实现。通过 key 快速查询、修改、删除对应 value,是开发高频容器。

1.1 核心特性

  1. 无序性:遍历 Map 时,键值对输出顺序不固定,和写入顺序无关;
  2. 键唯一性 :同一个 Map 中 key 不可重复,重复赋值会覆盖原有 value;
  3. 零值规则:查询不存在的 key,返回 value 对应类型的零值;
  4. 引用类型:Map 赋值、函数传参时,多个变量指向同一底层数据,修改互相影响;
  5. 键类型限制key 必须是可比较类型(整型、字符串、布尔、数组等),切片、Map、函数不可作为 key

1.2 Map 创建方式

方式1:make 函数创建(工程首选)

语法:

go 复制代码
make(map[键类型]值类型 [初始容量])
  • 初始容量为可选参数,达到容量后 Map 自动扩容;
  • 仅声明未使用 make 初始化的 Map 为 nil无法直接增删元素

示例:

go 复制代码
package main
import "fmt"

func main() {
    // 无初始容量
    m1 := make(map[string]int)
    // 指定初始容量 10
    m2 := make(map[string]int, 10)

    m1["苹果"] = 1
    fmt.Println(m1)
}

方式2:字面量直接初始化

适合已知初始键值对的场景:

go 复制代码
package main
import "fmt"

func main() {
    // 初始化并赋值
    m := map[string]int{
        "apple":  1,
        "banana": 2,
        "orange": 3,
    }
    fmt.Println(m)
}

方式3:仅声明(nil Map)

go 复制代码
var m map[string]string // 默认为 nil,不能直接存数据
// m["test"] = "123"  // 运行报错

1.3 Map 基础操作

1. 增 / 改 元素

直接通过 map[key] = value 赋值:

  • key 不存在 → 新增键值对;
  • key 已存在 → 覆盖原有 value。
go 复制代码
siteMap := make(map[string]string)
siteMap["Runoob"] = "菜鸟教程" // 新增
siteMap["Runoob"] = "新名称"   // 修改覆盖

2. 查询元素

两种写法:

  1. 单返回值:直接取值,key 不存在返回零值,无法判断 key 是否存在
  2. 双返回值:value, ok := map[key]ok 为布尔值,true 表示 key 存在。
go 复制代码
package main
import "fmt"

func main() {
    m := map[string]int{"a": 10}
    // 写法1:单值获取
    v1 := m["a"]
    fmt.Println(v1)

    // 写法2:判断键是否存在(推荐)
    v2, ok := m["b"]
    if ok {
        fmt.Println("存在,值:", v2)
    } else {
        fmt.Println("键不存在")
    }
}

3. 获取长度

使用 len(map) 获取当前键值对总数:

go 复制代码
m := map[string]int{"a":1, "b":2}
fmt.Println(len(m)) // 输出 2

4. 遍历 Map

搭配 for-range 遍历,遍历顺序随机:

go 复制代码
package main
import "fmt"

func main() {
    m := map[string]string{
        "France": "巴黎",
        "Japan":  "东京",
    }
    // 遍历 key + value
    for k, v := range m {
        fmt.Printf("%s => %s\n", k, v)
    }
    // 只遍历 key
    for k := range m {
        fmt.Println(k)
    }
}

5. 删除元素

使用内置函数 delete(map, key)key 不存在不会报错

go 复制代码
package main
import "fmt"

func main() {
    m := map[string]string{"a": "111", "b": "222"}
    delete(m, "a") // 删除键 a
    fmt.Println(m)
    delete(m, "c") // 键不存在,无任何异常
}

1.4 Map 高频踩坑

  1. nil Map 直接赋值var m map[k]v 声明后未 make,直接 m[k]=v 运行 panic;
  2. 混淆"零值"和"真实值" :仅单值查询时,零值无法区分 key 不存在 & value 本身就是零值,必须用 ok 判断;
  3. key 类型非法:切片、Map、函数不能作为 Map 的键;
  4. 遍历中增删元素:遍历 Map 时修改/删除元素,会导致遍历结果异常;
  5. 引用传递特性:Map 传参/赋值后,多个变量共享底层数据,一处修改全局生效;
  6. Map 不支持下标遍历 :不能使用 for i := 0; i<len(m);i++ 方式遍历。

二、递归函数

递归是函数直接或间接调用自身的编程方式,适合拆解为重复子问题的场景(阶乘、数列、目录遍历等)。

2.1 递归两大必要条件

  1. 基准条件(终止条件) :递归停止的出口,必须设置,否则无限递归、栈溢出;
  2. 递归体:函数调用自身,将大问题拆解为同类型小问题。

2.2 基础语法

go 复制代码
func 函数名(参数) 返回值 {
    // 1. 基准条件:终止递归
    if 终止判断 {
        return 结果
    }
    // 2. 递归体:调用自身
    return 函数名(新参数)
}

2.3 经典递归示例

示例1:计算阶乘 n!

规则:0! = 1n! = n * (n-1)!

go 复制代码
package main
import "fmt"

// 递归计算阶乘
func factorial(n int) int {
    // 基准条件
    if n == 0 {
        return 1
    }
    // 递归调用自身
    return n * factorial(n-1)
}

func main() {
    fmt.Println(factorial(5)) // 输出 120
}

示例2:斐波那契数列

规则:f(0)=0,f(1)=1,f(n)=f(n-2)+f(n-1)

go 复制代码
package main
import "fmt"

func fibonacci(n int) int {
    if n < 2 {
        return n
    }
    return fibonacci(n-2) + fibonacci(n-1)
}

func main() {
    for i := 0; i < 10; i++ {
        fmt.Printf("%d ", fibonacci(i))
    }
    // 输出:0 1 1 2 3 5 8 13 21 34
}

示例3:递归遍历文件目录

go 复制代码
package main
import (
    "fmt"
    "os"
    "path/filepath"
)

// 递归遍历目录
func walkDir(dir string, indent string) {
    entries, err := os.ReadDir(dir)
    if err != nil {
        return
    }
    for _, entry := range entries {
        fmt.Println(indent + entry.Name())
        // 如果是文件夹,递归进入子目录
        if entry.IsDir() {
            walkDir(filepath.Join(dir, entry.Name()), indent+"  ")
        }
    }
}

func main() {
    walkDir(".", "")
}

2.4 递归优缺点 & 对比迭代

对比项 递归 迭代(循环)
代码 简洁、逻辑贴近问题本身 代码偏冗长
性能 函数调用开销大,深度递归易栈溢出 执行效率高,内存占用小
调试 调用链路深,调试困难 流程直观,易调试

适用场景

  • 优先递归:树、图遍历、分治算法、目录遍历;
  • 优先迭代:大数据、深度循环、性能敏感场景。

2.5 递归高频踩坑

  1. 缺少终止条件 :无限递归,触发 goroutine stack exceeds 栈溢出崩溃;
  2. 终止条件错误:递归无法正常退出,结果异常;
  3. 递归深度过大:Go 栈空间有限,深度递归直接栈溢出;
  4. 重复计算:如斐波那契递归写法,存在大量重复运算,性能极差。

三、类型转换

Go 是强类型语言,不支持隐式类型转换,不同类型之间赋值、运算必须手动显式转换。分为:基础数值转换、字符串互转、接口类型断言/转换三大类。

3.1 基础数值类型转换

语法

go 复制代码
目标类型(表达式/变量)

示例

go 复制代码
package main
import "fmt"

func main() {
    var a int = 17
    var b int = 5
    // 整型转浮点型后再运算,避免整数除法
    res := float32(a) / float32(b)
    fmt.Println(res) // 3.4
}

规则 & 踩坑

  1. 不同位数整型(int/int32/int64)必须手动转换,直接赋值编译报错;
  2. 大范围转小范围会截断数据(精度丢失);
  3. 浮点转整型会直接舍弃小数部分,不四舍五入。

错误示例:

go 复制代码
var a int64 = 10
var b int32
// b = a  编译报错,不支持隐式转换
b = int32(a) // 正确显式转换

3.2 字符串与基本类型互转

依赖标准库 strconv 包,不能直接用基础类型转换语法。

1. 字符串 ↔ 整型

  • strconv.Atoi(s):字符串转 int,返回 (数值, 错误)
  • strconv.Itoa(n):int 转字符串。
go 复制代码
package main
import (
    "fmt"
    "strconv"
)

func main() {
    // 字符串转整型
    str1 := "123"
    num, err := strconv.Atoi(str1)
    if err == nil {
        fmt.Println(num)
    }

    // 整型转字符串
    num2 := 456
    str2 := strconv.Itoa(num2)
    fmt.Println(str2)
}

2. 字符串 ↔ 浮点型

  • strconv.ParseFloat(字符串, 精度):字符串转浮点;
  • strconv.FormatFloat(浮点数, 格式, 小数位数, 精度):浮点转字符串。
go 复制代码
package main
import (
    "fmt"
    "strconv"
)

func main() {
    // 字符串转 float64
    s := "3.14"
    f, _ := strconv.ParseFloat(s, 64)
    fmt.Println(f)

    // float64 转字符串,保留2位小数
    f2 := 3.1415
    s2 := strconv.FormatFloat(f2, 'f', 2, 64)
    fmt.Println(s2)
}

踩坑

  1. 非数字字符串调用 Atoi/ParseFloat,会返回转换错误;
  2. 不能直接用 string(数字) 做数字转字符串(会转为 ASCII 字符,不是数字文本)。

3.3 接口类型转换(类型断言)

空接口 interface{} 可接收任意类型值,使用类型断言还原原始类型。

基础语法

go 复制代码
// 写法1:判断类型并取值
值, 布尔标识 := 接口变量.(目标类型)

// 写法2:类型选择(switch 批量判断类型)
switch 变量 := 接口变量.(type) {
    case 类型1:
        // 对应逻辑
    case 类型2:
        // 对应逻辑
    default:
}

示例1:基础类型断言

go 复制代码
package main
import "fmt"

func main() {
    var i interface{} = "Go语言"
    // 断言为字符串类型
    str, ok := i.(string)
    if ok {
        fmt.Println("转换成功:", str)
    } else {
        fmt.Println("类型不匹配")
    }
}

示例2:类型选择(多类型判断)

go 复制代码
package main
import "fmt"

func printVal(v interface{}) {
    switch v := v.(type) {
    case int:
        fmt.Println("整型:", v)
    case string:
        fmt.Println("字符串:", v)
    case float64:
        fmt.Println("浮点型:", v)
    default:
        fmt.Println("未知类型")
    }
}

func main() {
    printVal(100)
    printVal("test")
    print(3.14)
}

3.4 类型转换通用踩坑

  1. 禁止隐式转换:所有不同类型必须手动转换,编译强制校验;
  2. 数值精度丢失:大类型转小类型、浮点转整型会丢失数据;
  3. 字符串数字转换:非数字文本转换必然报错,必须捕获 error;
  4. 类型断言失败 :不使用 ok 直接断言,类型不匹配会触发运行 panic;
  5. 区分转换语法 :数值用类型(),字符串/数字互转用strconv包,接口用类型断言。

四、知识点速记

模块 核心要点 高频坑点
Map 无序键值对、引用类型;make初始化;delete删除;range遍历 nil Map直接赋值;仅用单值判断键是否存在;切片/Map做key
递归 必须有终止条件;函数调用自身;适合分治/遍历 无终止条件导致栈溢出;深度递归性能差
类型转换 强类型、无隐式转换;数值直接转;字符串用strconv;接口用类型断言 类型不匹配直接赋值;断言不判断ok;非法字符串转数字
相关推荐
Scott9999HH40 分钟前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社2 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海2 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨3 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
a1117765 小时前
2FA 验证码生成器(github登录验证 app)
笔记·学习
我的xiaodoujiao5 小时前
API 接口自动化测试详细图文教程学习系列32--Allure测试报告2
python·学习·测试工具·pytest
人邮异步社区5 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大6 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai6 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang