Go 语言从入门到进阶:4. 数组和MAP使用方法总结

Go 语言数组完全使用指南

一、基础操作

1. 声明和初始化
go 复制代码
// 声明方式
var arr1 [5]int                         // 默认零值
arr2 := [3]int{1, 2, 3}                 // 字面量
arr3 := [...]int{1, 2, 3, 4}           // 编译器推断长度
arr4 := [5]int{1, 2}                   // 部分初始化,其余为零值
arr5 := [5]int{2: 10, 4: 20}           // 指定索引初始化
2. 访问和修改元素
go 复制代码
arr := [5]int{1, 2, 3, 4, 5}
value := arr[0]    // 获取索引0的值
arr[1] = 10        // 修改索引1的值
length := len(arr) // 获取数组长度

二、遍历操作

go 复制代码
arr := [5]int{1, 2, 3, 4, 5}

// 方式1:for 循环
for i := 0; i < len(arr); i++ {
    fmt.Println(arr[i])
}

// 方式2:range(推荐)
for index, value := range arr {
    fmt.Printf("索引: %d, 值: %d\n", index, value)
}

// 方式3:只遍历值
for _, value := range arr {
    fmt.Println(value)
}

三、常用操作函数

1. 比较操作
go 复制代码
arr1 := [3]int{1, 2, 3}
arr2 := [3]int{1, 2, 3}
arr3 := [3]int{4, 5, 6}

equal := arr1 == arr2  // true
equal = arr1 == arr3   // false
// 注意:只有相同长度和类型的数组才能比较
2. 复制数组
go 复制代码
// 值拷贝(深拷贝)
original := [3]int{1, 2, 3}
copy1 := original      // 完全复制
copy2 := original      // 独立副本

copy1[0] = 100         // 不影响 original
fmt.Println(original)  // [1, 2, 3]
3. 数组排序(需要转换为切片)
go 复制代码
import "sort"

arr := [5]int{5, 2, 8, 1, 9}
slice := arr[:]              // 转换为切片
sort.Ints(slice)             // 升序排序
// arr 变为 [1, 2, 5, 8, 9]

// 降序排序
sort.Sort(sort.Reverse(sort.IntSlice(slice)))

四、数组和切片的转换

go 复制代码
arr := [5]int{1, 2, 3, 4, 5}

// 数组转切片
slice1 := arr[:]      // 所有元素
slice2 := arr[1:4]    // [2,3,4]
slice3 := arr[2:]     // [3,4,5]
slice4 := arr[:3]     // [1,2,3]

// 切片不能直接转数组,但可以复制
var newArr [3]int
copy(newArr[:], slice2)  // 通过切片复制

五、多维数组操作

go 复制代码
// 二维数组
matrix := [3][3]int{
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9},
}

// 遍历二维数组
for i := 0; i < len(matrix); i++ {
    for j := 0; j < len(matrix[i]); j++ {
        fmt.Printf("%d ", matrix[i][j])
    }
    fmt.Println()
}

// 使用 range
for _, row := range matrix {
    for _, val := range row {
        fmt.Printf("%d ", val)
    }
    fmt.Println()
}

六、实用工具函数

go 复制代码
// 1. 查找元素
func contains(arr [5]int, target int) bool {
    for _, v := range arr {
        if v == target {
            return true
        }
    }
    return false
}

// 2. 求和
func sum(arr [5]int) int {
    total := 0
    for _, v := range arr {
        total += v
    }
    return total
}

// 3. 最大值/最小值
func max(arr [5]int) int {
    maxVal := arr[0]
    for _, v := range arr {
        if v > maxVal {
            maxVal = v
        }
    }
    return maxVal
}

// 4. 反转数组
func reverse(arr *[5]int) {
    for i, j := 0, len(arr)-1; i < j; i, j = i+1, j-1 {
        arr[i], arr[j] = arr[j], arr[i]
    }
}

七、重要特性说明

  1. 数组是值类型:传递数组时会复制整个数组

    go 复制代码
    func modify(arr [3]int) {
        arr[0] = 100  // 不影响原数组
    }
  2. 使用指针避免复制

    go 复制代码
    func modifyPtr(arr *[3]int) {
        arr[0] = 100  // 影响原数组
    }
  3. 数组长度是类型的一部分

    go 复制代码
    var a [3]int
    var b [4]int
    // a = b  // 编译错误:类型不同

八、最佳实践建议

  1. 大多数情况使用切片代替数组:切片更灵活,功能更强
  2. 数组适合固定大小的数据:如月份天数、RGB 颜色值
  3. 大数组传递时使用指针:避免性能开销
  4. 使用 [...T] 让编译器计算长度:增加代码可维护性
go 复制代码
// 推荐:让编译器计算长度
days := [...]string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}

// 不推荐:手动计数易错
days := [7]string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}

Go 语言 Map 完全使用指南

一、声明和初始化

go 复制代码
// 格式:var 变量名 map[键类型]值类型
var m map[string]int // 键:string,值:int
// 此时 m = nil,不能直接赋值,会 panic

// 1. 使用 make 创建(推荐)
m1 := make(map[string]int)
m2 := make(map[string]int, 10)  // 预分配容量,提高性能

// 2. 字面量创建
m3 := map[string]int{
    "apple":  5,
    "banana": 3,
    "orange": 8,
}

// 3. 声明 nil map
var m4 map[string]int  // nil map,不能直接赋值
// m4["key"] = 1  // 会 panic!

// 4. 创建空 map
m5 := map[string]int{}  // 空 map,可以赋值

二、基本操作

1. 增删改查
go 复制代码
m := make(map[string]int)

// 添加/修改
m["apple"] = 5      // 添加
m["apple"] = 10     // 修改

// 查询
value := m["apple"]           // 如果键不存在,返回值类型的零值
value, exists := m["apple"]   // 推荐:检查键是否存在

if value, ok := m["banana"]; ok {
    fmt.Println("存在:", value)
} else {
    fmt.Println("不存在")
}

// 删除
delete(m, "apple")   // 删除键 "apple"
delete(m, "none")    // 删除不存在的键,不会 panic

// 清空 map
for k := range m {
    delete(m, k)
}
// 或直接重新分配
m = make(map[string]int)
2. 长度获取
go 复制代码
m := map[string]int{"a": 1, "b": 2, "c": 3}
length := len(m)  // 3

三、遍历操作

go 复制代码
m := map[string]int{
    "apple":  5,
    "banana": 3,
    "orange": 8,
}

// 遍历键值对
for key, value := range m {
    fmt.Printf("%s: %d\n", key, value)
}

// 只遍历键
for key := range m {
    fmt.Println(key)
}

// 只遍历值
for _, value := range m {
    fmt.Println(value)
}

// 注意:map 遍历顺序是随机的!
// 如需有序遍历,先获取所有键并排序

四、常用操作函数

1. 键值对操作
go 复制代码
// 获取所有键
func getKeys(m map[string]int) []string {
    keys := make([]string, 0, len(m))
    for k := range m {
        keys = append(keys, k)
    }
    return keys
}

// 获取所有值
func getValues(m map[string]int) []int {
    values := make([]int, 0, len(m))
    for _, v := range m {
        values = append(values, v)
    }
    return values
}

// 合并 map
func merge(m1, m2 map[string]int) map[string]int {
    result := make(map[string]int)
    for k, v := range m1 {
        result[k] = v
    }
    for k, v := range m2 {
        result[k] = v
    }
    return result
}
2. 查找和判断
go 复制代码
// 键是否存在
if value, exists := m["key"]; exists {
    // 键存在
}

// 判断 map 是否为空
func isEmpty(m map[string]int) bool {
    return len(m) == 0
}

// 包含某个值
func containsValue(m map[string]int, target int) bool {
    for _, v := range m {
        if v == target {
            return true
        }
    }
    return false
}

五、Map 的排序

go 复制代码
import "sort"

m := map[string]int{
    "banana": 3,
    "apple":  5,
    "orange": 8,
}

// 1. 按键排序
keys := make([]string, 0, len(m))
for k := range m {
    keys = append(keys, k)
}
sort.Strings(keys)  // 升序

for _, k := range keys {
    fmt.Printf("%s: %d\n", k, m[k])
}

// 2. 按值排序
type kv struct {
    Key   string
    Value int
}

var sorted []kv
for k, v := range m {
    sorted = append(sorted, kv{k, v})
}
sort.Slice(sorted, func(i, j int) bool {
    return sorted[i].Value < sorted[j].Value  // 按值升序
})

六、嵌套 Map

go 复制代码
// 二维 map
nested := make(map[string]map[string]int)

// 初始化内层 map
nested["fruit"] = make(map[string]int)
nested["fruit"]["apple"] = 5
nested["fruit"]["banana"] = 3

// 更安全的访问
if fruitMap, ok := nested["fruit"]; ok {
    if value, ok := fruitMap["apple"]; ok {
        fmt.Println(value)
    }
}

// 字面量创建
nested2 := map[string]map[string]int{
    "fruit": {
        "apple":  5,
        "banana": 3,
    },
    "vegetable": {
        "carrot": 10,
    },
}

七、高级用法

1. Map 作为集合(Set)
go 复制代码
// Go 没有内置 Set,用 map 模拟
set := make(map[string]bool)

// 添加元素
set["apple"] = true
set["banana"] = true

// 检查存在
if set["apple"] {
    fmt.Println("apple 存在")
}

// 删除元素
delete(set, "apple")

// 遍历集合
for key := range set {
    fmt.Println(key)
}

// 使用空结构体节省内存
set2 := make(map[string]struct{})
set2["apple"] = struct{}{}
2. Map 作为缓存
go 复制代码
type Cache struct {
    data map[string]interface{}
    mu   sync.RWMutex  // 并发安全
}

func NewCache() *Cache {
    return &Cache{
        data: make(map[string]interface{}),
    }
}

func (c *Cache) Set(key string, value interface{}) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.data[key] = value
}

func (c *Cache) Get(key string) (interface{}, bool) {
    c.mu.RLock()
    defer c.mu.RUnlock()
    val, ok := c.data[key]
    return val, ok
}
3. 值为函数
go 复制代码
// 函数映射
type Handler func(string) string

handlers := map[string]Handler{
    "upper": func(s string) string { return strings.ToUpper(s) },
    "lower": func(s string) string { return strings.ToLower(s) },
    "reverse": func(s string) string {
        runes := []rune(s)
        for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
            runes[i], runes[j] = runes[j], runes[i]
        }
        return string(runes)
    },
}

// 调用
if handler, ok := handlers["upper"]; ok {
    result := handler("hello")  // "HELLO"
}

八、并发安全

go 复制代码
// 普通 map 非并发安全(会 panic)
// 方式1:使用 sync.RWMutex
type SafeMap struct {
    mu sync.RWMutex
    m  map[string]int
}

func (s *SafeMap) Get(key string) (int, bool) {
    s.mu.RLock()
    defer s.mu.RUnlock()
    val, ok := s.m[key]
    return val, ok
}

func (s *SafeMap) Set(key string, value int) {
    s.mu.Lock()
    defer s.mu.Unlock()
    s.m[key] = value
}

// 方式2:使用 sync.Map(适合读多写少)
var syncMap sync.Map

// 存储
syncMap.Store("key", "value")

// 加载
if value, ok := syncMap.Load("key"); ok {
    fmt.Println(value)
}

// 删除
syncMap.Delete("key")

// 遍历
syncMap.Range(func(key, value interface{}) bool {
    fmt.Println(key, value)
    return true  // 返回 false 停止遍历
})

九、性能优化技巧

go 复制代码
// 1. 预分配容量
m := make(map[string]int, 1000)  // 避免频繁扩容

// 2. 使用适当的数据类型
// 整型键比字符串快
m := make(map[int]string)

// 3. 避免在循环中创建 map
// 不好
for i := 0; i < 1000; i++ {
    m := make(map[string]int)  // 重复创建
}

// 好
m := make(map[string]int)
for i := 0; i < 1000; i++ {
    m["key"] = i  // 复用
    delete(m, "key")
}

// 4. 复用 map
func clearMap(m map[string]int) {
    for k := range m {
        delete(m, k)
    }
    // 相比重新分配,删除所有元素可能更高效
}

十、注意事项和最佳实践

go 复制代码
// 1. nil map 检查
func processMap(m map[string]int) {
    if m == nil {
        m = make(map[string]int)  // 初始化 nil map
    }
    m["key"] = 1
}

// 2. 读取不存在的键返回零值
m := map[string]int{}
value := m["none"]  // 0,不会报错

// 3. map 不能直接比较
// m1 == m2  // 编译错误!只能和 nil 比较
func equal(m1, m2 map[string]int) bool {
    if len(m1) != len(m2) {
        return false
    }
    for k, v1 := range m1 {
        if v2, ok := m2[k]; !ok || v1 != v2 {
            return false
        }
    }
    return true
}

// 4. 使用复合字面量初始化
m := map[string]int{
    "key1": 1,
    "key2": 2,
    // 注意最后的逗号必须有
}

十一、实用示例

go 复制代码
// 1. 统计字符频率
func charCount(s string) map[rune]int {
    count := make(map[rune]int)
    for _, ch := range s {
        count[ch]++
    }
    return count
}

// 2. 分组
func groupByLength(words []string) map[int][]string {
    groups := make(map[int][]string)
    for _, word := range words {
        length := len(word)
        groups[length] = append(groups[length], word)
    }
    return groups
}

// 3. 反转 map(值唯一时)
func reverseMap(m map[string]int) map[int]string {
    reversed := make(map[int]string, len(m))
    for k, v := range m {
        reversed[v] = k
    }
    return reversed
}

// 4. 深拷贝 map
func deepCopy(original map[string]int) map[string]int {
    copy := make(map[string]int, len(original))
    for k, v := range original {
        copy[k] = v
    }
    return copy
}

总结要点

  1. 使用 make 创建,避免 nil map
  2. 读取时用 value, ok := m[key] 判断存在性
  3. 遍历顺序随机,需要排序时先获取键切片
  4. map 是引用类型,传递时不复制内容
  5. 非并发安全 ,并发场景使用 sync.Map 或互斥锁
  6. 不能使用 == 比较,需要自己实现比较函数
  7. 预分配容量提升性能
  8. 值类型可以是任意类型,包括函数、结构体等
相关推荐
qq_2518364571 小时前
SpringBoot+Vue 共享电池柜管理系统 完整实现 前后端分离项目实战 完整代码
vue.js·spring boot·后端
春生野草1 小时前
反射、Tomcat执行
java·开发语言
zhangxingchao1 小时前
AI 大模型核心六:量化、Workflow 与 Agent、多轮 RAG
前端·人工智能·后端
雪的季节2 小时前
企业级 Qt 全功能项目
开发语言·数据库·qt
IT_陈寒2 小时前
Vite打包时遇到的坑,原来问题出在这里
前端·人工智能·后端
代龙涛3 小时前
WordPress page.php 页面模板与自定义模板使用方法
android·开发语言·php
bigfootyazi3 小时前
python爬虫-基本库-urllib库(常用速查)
开发语言·爬虫·python
ayqy贾杰3 小时前
基层管理的三板斧,在AI时代行不通了
前端·后端·团队管理
Apifox3 小时前
Apifox 5 月更新|Postman 导入优化、Runner 支持非 root 运行、请求代码自动带鉴权
前端·后端·安全