Skip to content

Go 语言集合(Map)

映射(Map)是 Go 语言中的关联数组,用于存储键值对。它提供了快速的查找、插入和删除操作,是构建高效程序的重要数据结构。

📋 映射基础

映射声明和初始化

go
package main

import "fmt"

func main() {
    // 1. 声明映射(零值为 nil)
    var m1 map[string]int
    fmt.Printf("零值映射: %v, 是否为nil: %t\n", m1, m1 == nil)
    
    // 2. 使用 make 创建映射
    m2 := make(map[string]int)
    fmt.Printf("make映射: %v, 长度: %d\n", m2, len(m2))
    
    // 3. 映射字面量初始化
    m3 := map[string]int{
        "apple":  5,
        "banana": 3,
        "orange": 8,
    }
    fmt.Printf("字面量映射: %v, 长度: %d\n", m3, len(m3))
    
    // 4. 空映射初始化
    m4 := map[string]int{}
    fmt.Printf("空映射: %v, 长度: %d\n", m4, len(m4))
    
    // 5. 不同类型的映射
    ages := map[string]int{
        "Alice": 25,
        "Bob":   30,
        "Carol": 28,
    }
    
    scores := map[int]float64{
        1001: 95.5,
        1002: 87.3,
        1003: 92.1,
    }
    
    fmt.Printf("年龄映射: %v\n", ages)
    fmt.Printf("分数映射: %v\n", scores)
}

映射操作

go
func main() {
    // 创建映射
    fruits := make(map[string]int)
    
    // 添加元素
    fruits["apple"] = 10
    fruits["banana"] = 5
    fruits["orange"] = 8
    
    fmt.Printf("初始映射: %v\n", fruits)
    
    // 访问元素
    appleCount := fruits["apple"]
    fmt.Printf("苹果数量: %d\n", appleCount)
    
    // 修改元素
    fruits["apple"] = 15
    fmt.Printf("修改后: %v\n", fruits)
    
    // 检查键是否存在
    count, exists := fruits["grape"]
    if exists {
        fmt.Printf("葡萄数量: %d\n", count)
    } else {
        fmt.Printf("葡萄不存在,返回零值: %d\n", count)
    }
    
    // 添加新元素
    fruits["grape"] = 12
    fmt.Printf("添加葡萄后: %v\n", fruits)
    
    // 删除元素
    delete(fruits, "banana")
    fmt.Printf("删除香蕉后: %v\n", fruits)
    
    // 映射长度
    fmt.Printf("映射长度: %d\n", len(fruits))
}

🔍 映射遍历

遍历键值对

go
func main() {
    studentGrades := map[string]float64{
        "Alice":   95.5,
        "Bob":     87.3,
        "Carol":   92.1,
        "David":   78.9,
        "Eve":     88.7,
    }
    
    fmt.Println("遍历所有键值对:")
    for name, grade := range studentGrades {
        fmt.Printf("学生: %s, 成绩: %.1f\n", name, grade)
    }
    
    fmt.Println("\n只遍历键:")
    for name := range studentGrades {
        fmt.Printf("学生: %s\n", name)
    }
    
    fmt.Println("\n只遍历值:")
    for _, grade := range studentGrades {
        fmt.Printf("成绩: %.1f\n", grade)
    }
    
    // 注意:映射的遍历顺序是随机的
    fmt.Println("\n多次遍历展示随机顺序:")
    for i := 0; i < 3; i++ {
        fmt.Printf("第%d次遍历: ", i+1)
        for name := range studentGrades {
            fmt.Printf("%s ", name)
        }
        fmt.Println()
    }
}

有序遍历

go
import (
    "fmt"
    "sort"
)

func main() {
    data := map[string]int{
        "charlie": 3,
        "alice":   1,
        "bob":     2,
        "dave":    4,
    }
    
    fmt.Printf("原始映射: %v\n", data)
    
    // 按键排序遍历
    var keys []string
    for key := range data {
        keys = append(keys, key)
    }
    sort.Strings(keys)
    
    fmt.Println("按键排序遍历:")
    for _, key := range keys {
        fmt.Printf("%s: %d\n", key, data[key])
    }
    
    // 按值排序遍历
    type KeyValue struct {
        Key   string
        Value int
    }
    
    var kvPairs []KeyValue
    for key, value := range data {
        kvPairs = append(kvPairs, KeyValue{key, value})
    }
    
    // 按值排序
    sort.Slice(kvPairs, func(i, j int) bool {
        return kvPairs[i].Value < kvPairs[j].Value
    })
    
    fmt.Println("按值排序遍历:")
    for _, kv := range kvPairs {
        fmt.Printf("%s: %d\n", kv.Key, kv.Value)
    }
}

🔧 高级映射操作

映射的映射

go
func main() {
    // 嵌套映射:学生的各科成绩
    studentScores := map[string]map[string]float64{
        "Alice": {
            "Math":    95.0,
            "English": 88.5,
            "Science": 92.3,
        },
        "Bob": {
            "Math":    87.5,
            "English": 91.2,
            "Science": 89.8,
        },
    }
    
    fmt.Println("学生成绩:")
    for student, scores := range studentScores {
        fmt.Printf("%s的成绩:\n", student)
        for subject, score := range scores {
            fmt.Printf("  %s: %.1f\n", subject, score)
        }
    }
    
    // 添加新学生
    studentScores["Carol"] = make(map[string]float64)
    studentScores["Carol"]["Math"] = 93.7
    studentScores["Carol"]["English"] = 89.4
    studentScores["Carol"]["Science"] = 95.1
    
    // 修改成绩
    studentScores["Alice"]["Math"] = 97.0
    
    // 安全访问嵌套映射
    if aliceScores, exists := studentScores["Alice"]; exists {
        if mathScore, hasMatch := aliceScores["Math"]; hasMatch {
            fmt.Printf("Alice的数学成绩: %.1f\n", mathScore)
        }
    }
    
    // 计算平均分
    for student, scores := range studentScores {
        total := 0.0
        count := 0
        for _, score := range scores {
            total += score
            count++
        }
        average := total / float64(count)
        fmt.Printf("%s的平均分: %.2f\n", student, average)
    }
}

映射作为集合

go
func main() {
    // 使用映射实现集合功能
    set1 := make(map[string]bool)
    set2 := make(map[string]bool)
    
    // 添加元素到集合
    fruits1 := []string{"apple", "banana", "orange", "grape"}
    fruits2 := []string{"banana", "orange", "kiwi", "mango"}
    
    for _, fruit := range fruits1 {
        set1[fruit] = true
    }
    
    for _, fruit := range fruits2 {
        set2[fruit] = true
    }
    
    fmt.Printf("集合1: %v\n", getKeys(set1))
    fmt.Printf("集合2: %v\n", getKeys(set2))
    
    // 交集
    intersection := make(map[string]bool)
    for key := range set1 {
        if set2[key] {
            intersection[key] = true
        }
    }
    fmt.Printf("交集: %v\n", getKeys(intersection))
    
    // 并集
    union := make(map[string]bool)
    for key := range set1 {
        union[key] = true
    }
    for key := range set2 {
        union[key] = true
    }
    fmt.Printf("并集: %v\n", getKeys(union))
    
    // 差集 (set1 - set2)
    difference := make(map[string]bool)
    for key := range set1 {
        if !set2[key] {
            difference[key] = true
        }
    }
    fmt.Printf("差集(1-2): %v\n", getKeys(difference))
}

// 获取映射的所有键
func getKeys(m map[string]bool) []string {
    keys := make([]string, 0, len(m))
    for key := range m {
        keys = append(keys, key)
    }
    return keys
}

映射的复制和比较

go
func main() {
    original := map[string]int{
        "a": 1,
        "b": 2,
        "c": 3,
    }
    
    // 浅复制
    shallowCopy := original
    fmt.Printf("原始映射: %v\n", original)
    fmt.Printf("浅复制: %v\n", shallowCopy)
    
    // 修改浅复制会影响原始映射
    shallowCopy["d"] = 4
    fmt.Printf("修改浅复制后:\n")
    fmt.Printf("原始映射: %v\n", original)
    fmt.Printf("浅复制: %v\n", shallowCopy)
    
    // 深复制
    deepCopy := make(map[string]int)
    for key, value := range original {
        deepCopy[key] = value
    }
    
    fmt.Printf("深复制: %v\n", deepCopy)
    
    // 修改深复制不会影响原始映射
    deepCopy["e"] = 5
    fmt.Printf("修改深复制后:\n")
    fmt.Printf("原始映射: %v\n", original)
    fmt.Printf("深复制: %v\n", deepCopy)
    
    // 映射比较(只能与 nil 比较)
    var nilMap map[string]int
    fmt.Printf("原始映射是否为nil: %t\n", original == nil)
    fmt.Printf("nil映射是否为nil: %t\n", nilMap == nil)
    
    // 自定义映射比较函数
    map1 := map[string]int{"a": 1, "b": 2}
    map2 := map[string]int{"a": 1, "b": 2}
    map3 := map[string]int{"a": 1, "b": 3}
    
    fmt.Printf("map1 == map2: %t\n", compareMaps(map1, map2))
    fmt.Printf("map1 == map3: %t\n", compareMaps(map1, map3))
}

// 自定义映射比较函数
func compareMaps(m1, m2 map[string]int) bool {
    if len(m1) != len(m2) {
        return false
    }
    
    for key, value1 := range m1 {
        if value2, exists := m2[key]; !exists || value1 != value2 {
            return false
        }
    }
    
    return true
}

🎯 实际应用示例

单词统计器

go
import (
    "fmt"
    "regexp"
    "sort"
    "strings"
)

type WordCounter struct {
    words map[string]int
    total int
}

func NewWordCounter() *WordCounter {
    return &WordCounter{
        words: make(map[string]int),
        total: 0,
    }
}

func (wc *WordCounter) AddText(text string) {
    // 转换为小写并提取单词
    text = strings.ToLower(text)
    reg := regexp.MustCompile(`[a-z]+`)
    words := reg.FindAllString(text, -1)
    
    for _, word := range words {
        wc.words[word]++
        wc.total++
    }
}

func (wc *WordCounter) GetWordCount(word string) int {
    return wc.words[strings.ToLower(word)]
}

func (wc *WordCounter) GetTotalWords() int {
    return wc.total
}

func (wc *WordCounter) GetUniqueWords() int {
    return len(wc.words)
}

func (wc *WordCounter) GetTopWords(n int) []struct {
    Word  string
    Count int
} {
    type WordCount struct {
        Word  string
        Count int
    }
    
    var wordCounts []WordCount
    for word, count := range wc.words {
        wordCounts = append(wordCounts, WordCount{word, count})
    }
    
    // 按计数排序
    sort.Slice(wordCounts, func(i, j int) bool {
        return wordCounts[i].Count > wordCounts[j].Count
    })
    
    if n > len(wordCounts) {
        n = len(wordCounts)
    }
    
    return wordCounts[:n]
}

func (wc *WordCounter) GetWordFrequency(word string) float64 {
    count := wc.GetWordCount(word)
    if wc.total == 0 {
        return 0
    }
    return float64(count) / float64(wc.total) * 100
}

func main() {
    counter := NewWordCounter()
    
    // 添加文本
    texts := []string{
        "Go is a programming language developed by Google.",
        "Go is simple, fast, and reliable.",
        "Programming in Go is fun and productive.",
        "Google created Go for modern software development.",
    }
    
    for _, text := range texts {
        counter.AddText(text)
    }
    
    // 统计结果
    fmt.Printf("总词数: %d\n", counter.GetTotalWords())
    fmt.Printf("唯一词数: %d\n", counter.GetUniqueWords())
    
    // 查询特定单词
    queryWords := []string{"go", "programming", "google", "python"}
    fmt.Println("\n单词查询:")
    for _, word := range queryWords {
        count := counter.GetWordCount(word)
        frequency := counter.GetWordFrequency(word)
        fmt.Printf("%s: 出现%d次, 频率%.2f%%\n", word, count, frequency)
    }
    
    // 高频词汇
    fmt.Println("\n前5个高频词汇:")
    topWords := counter.GetTopWords(5)
    for i, wc := range topWords {
        fmt.Printf("%d. %s: %d\n", i+1, wc.Word, wc.Count)
    }
}

缓存系统

go
import (
    "fmt"
    "sync"
    "time"
)

// 缓存项
type CacheItem struct {
    Value      interface{}
    Expiration time.Time
}

// 检查是否过期
func (item CacheItem) IsExpired() bool {
    return time.Now().After(item.Expiration)
}

// 简单缓存系统
type SimpleCache struct {
    items map[string]CacheItem
    mutex sync.RWMutex
}

func NewSimpleCache() *SimpleCache {
    cache := &SimpleCache{
        items: make(map[string]CacheItem),
    }
    
    // 启动清理过期项的 goroutine
    go cache.cleanupExpired()
    
    return cache
}

// 设置缓存项
func (c *SimpleCache) Set(key string, value interface{}, duration time.Duration) {
    c.mutex.Lock()
    defer c.mutex.Unlock()
    
    expiration := time.Now().Add(duration)
    c.items[key] = CacheItem{
        Value:      value,
        Expiration: expiration,
    }
}

// 获取缓存项
func (c *SimpleCache) Get(key string) (interface{}, bool) {
    c.mutex.RLock()
    defer c.mutex.RUnlock()
    
    item, exists := c.items[key]
    if !exists {
        return nil, false
    }
    
    if item.IsExpired() {
        delete(c.items, key)
        return nil, false
    }
    
    return item.Value, true
}

// 删除缓存项
func (c *SimpleCache) Delete(key string) {
    c.mutex.Lock()
    defer c.mutex.Unlock()
    
    delete(c.items, key)
}

// 获取缓存大小
func (c *SimpleCache) Size() int {
    c.mutex.RLock()
    defer c.mutex.RUnlock()
    
    return len(c.items)
}

// 清空缓存
func (c *SimpleCache) Clear() {
    c.mutex.Lock()
    defer c.mutex.Unlock()
    
    c.items = make(map[string]CacheItem)
}

// 获取所有键
func (c *SimpleCache) Keys() []string {
    c.mutex.RLock()
    defer c.mutex.RUnlock()
    
    keys := make([]string, 0, len(c.items))
    for key, item := range c.items {
        if !item.IsExpired() {
            keys = append(keys, key)
        }
    }
    
    return keys
}

// 清理过期项
func (c *SimpleCache) cleanupExpired() {
    ticker := time.NewTicker(1 * time.Minute)
    defer ticker.Stop()
    
    for range ticker.C {
        c.mutex.Lock()
        for key, item := range c.items {
            if item.IsExpired() {
                delete(c.items, key)
            }
        }
        c.mutex.Unlock()
    }
}

func main() {
    cache := NewSimpleCache()
    
    // 设置缓存项
    cache.Set("user:1001", "Alice", 5*time.Second)
    cache.Set("user:1002", "Bob", 10*time.Second)
    cache.Set("config:timeout", 30, 1*time.Minute)
    
    fmt.Printf("缓存大小: %d\n", cache.Size())
    fmt.Printf("所有键: %v\n", cache.Keys())
    
    // 获取缓存项
    if value, exists := cache.Get("user:1001"); exists {
        fmt.Printf("用户1001: %v\n", value)
    }
    
    if value, exists := cache.Get("config:timeout"); exists {
        fmt.Printf("超时配置: %v\n", value)
    }
    
    // 等待一些项过期
    fmt.Println("等待5秒...")
    time.Sleep(6 * time.Second)
    
    fmt.Printf("5秒后缓存大小: %d\n", cache.Size())
    fmt.Printf("5秒后所有键: %v\n", cache.Keys())
    
    // 再次获取过期的项
    if value, exists := cache.Get("user:1001"); exists {
        fmt.Printf("用户1001: %v\n", value)
    } else {
        fmt.Println("用户1001已过期")
    }
    
    if value, exists := cache.Get("user:1002"); exists {
        fmt.Printf("用户1002: %v\n", value)
    } else {
        fmt.Println("用户1002已过期")
    }
}

🎓 小结

本章我们全面学习了 Go 语言的映射:

  • 映射基础:声明、初始化、基本操作
  • 映射遍历:随机遍历、有序遍历
  • 高级操作:嵌套映射、映射作为集合
  • 实际应用:单词统计器、缓存系统

映射是 Go 语言中重要的数据结构,掌握映射的使用对于构建高效的程序至关重要。


接下来,我们将学习 Go 语言接口,了解 Go 语言的多态和抽象机制。

映射使用建议

  • 映射不是线程安全的,并发访问需要加锁
  • 映射的遍历顺序是随机的,不要依赖顺序
  • 检查键是否存在时使用 value, ok := map[key] 语法
  • 大量数据时考虑映射的内存占用

本站内容仅供学习和研究使用。