Map遍历 #

一、基本遍历 #

1.1 for-range遍历 #

go
m := map[string]int{"one": 1, "two": 2, "three": 3}

for key, value := range m {
    fmt.Printf("%s: %d\n", key, value)
}

1.2 只遍历键 #

go
m := map[string]int{"one": 1, "two": 2, "three": 3}

for key := range m {
    fmt.Println(key)
}

1.3 只遍历值 #

go
m := map[string]int{"one": 1, "two": 2, "three": 3}

for _, value := range m {
    fmt.Println(value)
}

二、遍历顺序 #

2.1 随机顺序 #

Map遍历顺序是随机的:

go
m := map[string]int{"a": 1, "b": 2, "c": 3}

for i := 0; i < 3; i++ {
    fmt.Println("---")
    for k, v := range m {
        fmt.Println(k, v)
    }
}

每次遍历的顺序可能不同。

2.2 为什么随机 #

Go故意随机化遍历顺序,防止程序依赖特定顺序。

三、有序遍历 #

3.1 按键排序 #

go
m := map[string]int{"c": 3, "a": 1, "b": 2}

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])
}

3.2 按值排序 #

go
m := map[string]int{"c": 3, "a": 1, "b": 2}

type pair struct {
    key   string
    value int
}

pairs := make([]pair, 0, len(m))
for k, v := range m {
    pairs = append(pairs, pair{k, v})
}

sort.Slice(pairs, func(i, j int) bool {
    return pairs[i].value < pairs[j].value
})

for _, p := range pairs {
    fmt.Printf("%s: %d\n", p.key, p.value)
}

3.3 使用结构体切片 #

go
type Entry struct {
    Key   string
    Value int
}

func sortedByValue(m map[string]int) []Entry {
    entries := make([]Entry, 0, len(m))
    for k, v := range m {
        entries = append(entries, Entry{k, v})
    }
    
    sort.Slice(entries, func(i, j int) bool {
        return entries[i].Value < entries[j].Value
    })
    
    return entries
}

四、遍历技巧 #

4.1 遍历时删除 #

go
m := map[string]int{"a": 1, "b": 2, "c": 3}

for k := range m {
    if k == "b" {
        delete(m, k)
    }
}
fmt.Println(m)

4.2 遍历时修改 #

go
m := map[string]int{"a": 1, "b": 2, "c": 3}

for k, v := range m {
    m[k] = v * 2
}
fmt.Println(m)

4.3 遍历时添加 #

遍历时添加新元素可能导致不确定行为:

go
m := map[string]int{"a": 1, "b": 2}

for k, v := range m {
    m[k+"_new"] = v * 2  // 不推荐
}

4.4 提前退出 #

go
m := map[string]int{"a": 1, "b": 2, "c": 3}

found := false
for k, v := range m {
    if v == 2 {
        fmt.Println("found:", k)
        found = true
        break
    }
}

五、嵌套Map遍历 #

5.1 二维Map遍历 #

go
m := map[string]map[string]int{
    "group1": {"a": 1, "b": 2},
    "group2": {"c": 3, "d": 4},
}

for group, subMap := range m {
    fmt.Println("Group:", group)
    for k, v := range subMap {
        fmt.Printf("  %s: %d\n", k, v)
    }
}

5.2 安全遍历嵌套Map #

go
for group, subMap := range m {
    if subMap == nil {
        continue
    }
    for k, v := range subMap {
        fmt.Printf("%s.%s: %d\n", group, k, v)
    }
}

六、遍历性能 #

6.1 预分配切片 #

go
keys := make([]string, 0, len(m))  // 预分配容量
for k := range m {
    keys = append(keys, k)
}

6.2 避免重复访问 #

go
// 不好:重复访问
for _, k := range keys {
    v := m[k]
    process(k, v)
    v2 := m[k]  // 重复访问
    process2(k, v2)
}

// 好:缓存值
for _, k := range keys {
    v := m[k]
    process(k, v)
    process2(k, v)
}

七、实际应用 #

7.1 统计并排序 #

go
func topN(m map[string]int, n int) []string {
    type pair struct {
        key   string
        value int
    }
    
    pairs := make([]pair, 0, len(m))
    for k, v := range m {
        pairs = append(pairs, pair{k, v})
    }
    
    sort.Slice(pairs, func(i, j int) bool {
        return pairs[i].value > pairs[j].value
    })
    
    result := make([]string, 0, n)
    for i := 0; i < n && i < len(pairs); i++ {
        result = append(result, pairs[i].key)
    }
    return result
}

7.2 分组统计 #

go
func groupCount(items []Item) map[string]int {
    groups := make(map[string]int)
    for _, item := range items {
        groups[item.Category]++
    }
    return groups
}

7.3 转换为切片 #

go
func mapToSlice(m map[string]int) []Entry {
    result := make([]Entry, 0, len(m))
    for k, v := range m {
        result = append(result, Entry{Key: k, Value: v})
    }
    return result
}

八、总结 #

Map遍历要点:

遍历方式 说明
for k, v := range m 遍历键值对
for k := range m 只遍历键
for _, v := range m 只遍历值

关键点:

  1. 随机顺序:Map遍历顺序不确定
  2. 有序遍历:需要先提取键并排序
  3. 遍历修改:可以修改值,但添加新键要小心
  4. 性能优化:预分配切片容量
  5. 嵌套遍历:注意检查nil

准备好学习Map实战了吗?让我们进入下一章!

最后更新:2026-03-26