Golang 5、Map 的基本用法_golang map key struct

Golang 5、Map 的基本用法_golang map key struct

编码文章call10242025-03-03 10:32:4632A+A-

在 Go 语言中,Map 是一种无序的键值对集合,用于存储和快速查找数据。它的键和值可以是任意类型,但键必须是可比较的类型(如字符串、整数等)。


1. Map 的基本概念

  • 键(Key): 用于查找值的唯一标识。
  • 值(Value): 与键关联的数据。
  • 特点:Map 是无序的,遍历时的顺序不固定。Map 是引用类型,零值为 nil。Map 的键必须是可比较的类型(如 string、int 等)。

2. Map 的声明和初始化

2.1 声明 Map

var m map[string]int // 声明一个键为 string、值为 int 的 Map
fmt.Println(m == nil) // 输出:true(未初始化的 Map 是 nil)

2.2 使用make初始化 Map

m := make(map[string]int) // 初始化一个空的 Map
fmt.Println(m == nil)     // 输出:false

2.3 直接初始化 Map

m := map[string]int{
    "apple":  1,
    "banana": 2,
    "orange": 3,
}
fmt.Println(m) // 输出:map[apple:1 banana:2 orange:3]

3. Map 的操作

3.1 添加或更新元素

通过键直接赋值:

m := make(map[string]int)
m["apple"] = 1 // 添加元素
m["banana"] = 2
m["apple"] = 3 // 更新元素
fmt.Println(m) // 输出:map[apple:3 banana:2]

3.2 获取元素

通过键获取值,如果键不存在,返回值类型的零值:

value := m["banana"]
fmt.Println(value) // 输出:2

value = m["grape"]
fmt.Println(value) // 输出:0(键不存在,返回 int 的零值)

3.3 检查键是否存在

使用多返回值判断键是否存在:

value, exists := m["banana"]
if exists {
    fmt.Println("Banana exists:", value)
} else {
    fmt.Println("Banana does not exist")
}

3.4 删除元素

使用 delete 函数删除键值对:

delete(m, "banana")
fmt.Println(m) // 输出:map[apple:3]

3.5 遍历 Map

使用 for range 遍历 Map:

for k, v := range m {
    fmt.Println(k, v)
}

注意:Map 是无序的,遍历顺序不固定。


4. Map 的长度和容量

  • 使用 len 获取 Map 中键值对的数量:
  • fmt.Println(len(m)) // 输出:2
  • Map 没有固定的容量概念,它会自动扩容。

5. Map 的性能

  • 查找插入删除 操作的平均时间复杂度为 O(1)。
  • 由于 Map 是基于哈希表实现的,性能非常高。

6. Map 的注意事项

6.1 并发安全

Map 不是并发安全的。在并发场景下,需要使用 sync.Map 或加锁(如 sync.Mutex)。

使用sync.Map

package main

import (
    "fmt"
    "sync"
)

func main() {
    var m sync.Map

    // 存储值
    m.Store("apple", 1)
    m.Store("banana", 2)

    // 加载值
    if value, ok := m.Load("banana"); ok {
        fmt.Println("Banana:", value) // 输出:Banana: 2
    }
}

使用sync.Mutex

package main

import (
    "fmt"
    "sync"
)

func main() {
    var m = make(map[string]int)
    var mu sync.Mutex

    // 添加值
    mu.Lock()
    m["apple"] = 1
    mu.Unlock()

    // 获取值
    mu.Lock()
    value := m["apple"]
    mu.Unlock()
    fmt.Println("Apple:", value) // 输出:Apple: 1
}

7. 完整示例代码

以下是一个完整的 Map 使用示例:

package main

import "fmt"

func main() {
    // 初始化 Map
    m := map[string]int{
        "apple":  1,
        "banana": 2,
        "orange": 3,
    }

    // 添加或更新元素
    m["grape"] = 4
    m["apple"] = 5

    // 获取元素
    fmt.Println("Apple:", m["apple"])

    // 检查键是否存在
    if value, exists := m["banana"]; exists {
        fmt.Println("Banana exists:", value)
    }

    // 删除元素
    delete(m, "orange")
    fmt.Println("After deletion:", m)

    // 遍历 Map
    fmt.Println("Traversing map:")
    for k, v := range m {
        fmt.Println(k, v)
    }

    // 获取 Map 的长度
    fmt.Println("Map length:", len(m))
}

8. map 是无序的?

Go 的 map 是基于哈希表(Hash Table)实现的。为了提高性能,Go 在底层对键进行哈希计算,然后将键值对存储到哈希表的特定位置。这种设计使得 map 的插入、删除和查找操作非常高效,但也导致了遍历时的无序性。


8.1 示例:map 的无序性

以下代码演示了 map 的遍历顺序是不固定的:

package main

import "fmt"

func main() {
    m := map[string]int{
        "apple":  1,
        "banana": 2,
        "orange": 3,
    }

    // 多次遍历 map
    for i := 0; i < 3; i++ {
        fmt.Printf("Iteration %d:\n", i+1)
        for k, v := range m {
            fmt.Println(k, v)
        }
        fmt.Println()
    }
}

可能的输出

Iteration 1:
banana 2
orange 3
apple 1

Iteration 2:
apple 1
banana 2
orange 3

Iteration 3:
orange 3
apple 1
banana 2

每次运行或多次遍历时,map 的输出顺序可能不同。


8.2. 如何实现有序的 map?

如果需要按特定顺序遍历 map,可以通过以下方式实现:

8.3 使用切片保存键的顺序

先将 map 的键保存到切片中,然后对切片排序,最后按顺序遍历 map。

package main

import (
    "fmt"
    "sort"
)

func main() {
    m := map[string]int{
        "apple":  1,
        "banana": 2,
        "orange": 3,
    }

    // 获取键的切片
    keys := make([]string, 0, len(m))
    for k := range m {
        keys = append(keys, k)
    }

    // 对键排序
    sort.Strings(keys)

    // 按顺序遍历 map
    for _, k := range keys {
        fmt.Println(k, m[k])
    }
}

输出

apple 1
banana 2
orange 3

8. 总结

  • Map 是 Go 语言中非常强大的数据结构,适用于快速查找和存储键值对。
  • Map 是无序的,遍历顺序不固定。
  • Map 不是并发安全的,并发场景下需使用 sync.Map 或 sync.Mutex。
点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4