ZetCode

Golang map 关键字

最后修改于 2025 年 5 月 7 日

本教程解释了如何在 Go 中使用 map 关键字。我们将通过创建和操作 map 的实际示例来介绍 map 的基础知识。

map 是一个内置的数据类型,它将键与值关联起来。它是 Go 对其他语言中哈希表或字典的实现。

在 Go 中,map 是无序集合,其中每个键都必须是唯一的。它们提供了高效的查找、插入和删除操作。

基本 map 声明和初始化

此示例展示了如何声明和初始化一个具有字符串键和整数值的 map。它演示了基本的 map 操作。

basic_map.go
package main

import "fmt"

func main() {
    // Declare and initialize a map
    ages := map[string]int{
        "Alice": 25,
        "Bob":   30,
        "Carol": 28,
    }

    // Access a value
    fmt.Println("Alice's age:", ages["Alice"])

    // Add a new key-value pair
    ages["Dave"] = 32

    // Update a value
    ages["Bob"] = 31

    // Delete a key
    delete(ages, "Carol")

    fmt.Println("Updated map:", ages)
}

map 使用三个键值对进行初始化。我们演示了访问、添加、更新和删除元素。Map 是动态的,可以根据需要增长。

检查键是否存在

此示例展示了如何检查 map 中是否存在某个键。Go 为此提供了特殊的语法。

key_check.go
package main

import "fmt"

func main() {
    colors := map[string]string{
        "red":   "#FF0000",
        "green": "#00FF00",
        "blue":  "#0000FF",
    }

    // Check if key exists
    value, exists := colors["red"]
    if exists {
        fmt.Println("Red's hex code:", value)
    } else {
        fmt.Println("Red not found")
    }

    // Check for non-existent key
    value, exists = colors["yellow"]
    if exists {
        fmt.Println("Yellow's hex code:", value)
    } else {
        fmt.Println("Yellow not found")
    }
}

双值赋值用于检查键是否存在。第二个布尔值指示键是否存在。这可以防止与零值混淆。

遍历 map

可以使用 range 关键字遍历 map。此示例展示了如何循环遍历所有键值对。

map_iteration.go
package main

import "fmt"

func main() {
    capitals := map[string]string{
        "USA":    "Washington D.C.",
        "France": "Paris",
        "Japan":  "Tokyo",
        "India":  "New Delhi",
    }

    // Iterate over all key-value pairs
    for country, capital := range capitals {
        fmt.Printf("The capital of %s is %s\n", country, capital)
    }

    // Iterate just over keys
    for country := range capitals {
        fmt.Println("Country:", country)
    }
}

range 关键字同时返回键和值。由于 map 是无序集合,因此不保证顺序。通过省略值可以单独访问键。

Map of slices

此示例演示了一个值是 slice 的 map。它展示了如何处理更复杂的 map 结构。

map_of_slices.go
package main

import "fmt"

func main() {
    // Map of string slices
    classes := map[string][]string{
        "math":    {"algebra", "calculus", "geometry"},
        "science": {"biology", "chemistry", "physics"},
    }

    // Add a new subject
    classes["history"] = []string{"world", "european", "american"}

    // Append to an existing slice
    classes["math"] = append(classes["math"], "statistics")

    // Print all subjects
    for subject, topics := range classes {
        fmt.Printf("%s: %v\n", subject, topics)
    }
}

map 将 slice 作为值存储。我们演示了初始化、添加新键以及向现有 slice 追加元素。这种模式对于分组数据很常见。

Map 长度和 nil maps

此示例涵盖了 map 长度以及 nil map 和空 map 之间的区别。理解这一点对于正确使用 map 至关重要。

map_length.go
package main

import "fmt"

func main() {
    // Nil map (cannot be used directly)
    var nilMap map[string]int
    fmt.Println("Nil map:", nilMap)

    // Empty initialized map
    emptyMap := make(map[string]int)
    fmt.Println("Empty map:", emptyMap)

    // Check length
    populatedMap := map[string]int{"a": 1, "b": 2}
    fmt.Println("Map length:", len(populatedMap))

    // Initialize nil map before use
    nilMap = make(map[string]int)
    nilMap["now"] = 1
    fmt.Println("Initialized nil map:", nilMap)
}

Nil maps 在初始化之前无法存储值。len 函数返回键值对的数量。始终在初始化后使用 map。

Map 作为函数参数

Map 是 Go 中的引用类型。此示例显示了将 map 传递给函数时它们的行为。

map_function.go
package main

import "fmt"

func updateMap(m map[string]int, key string, value int) {
    m[key] = value
}

func main() {
    scores := map[string]int{
        "Alice": 90,
        "Bob":   85,
    }

    fmt.Println("Before update:", scores)
    updateMap(scores, "Alice", 95)
    updateMap(scores, "Carol", 88)
    fmt.Println("After update:", scores)
}

Map 是通过引用传递的,因此函数内部的修改会影响原始 map。无需指针即可修改 map 内容。

并发 map 访问

此示例演示了在从多个 goroutine 访问 map 时进行同步的必要性。

concurrent_map.go
package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    var mu sync.Mutex
    sharedMap := make(map[int]int)

    // Write to map from multiple goroutines
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            mu.Lock()
            sharedMap[n] = n * n
            mu.Unlock()
        }(i)
    }

    wg.Wait()

    // Read from map
    mu.Lock()
    for k, v := range sharedMap {
        fmt.Printf("%d: %d\n", k, v)
    }
    mu.Unlock()
}

Map 不是线程安全的。并发访问需要使用互斥锁进行同步。此示例使用 sync.Mutex 来保护 map 操作。

来源

Go 语言规范

本教程通过各种场景下的 map 操作实际示例,讲解了 Go 中的 map 关键字。

作者

我的名字是 Jan Bodnar,我是一名充满热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出所有 Golang 教程