Go maps 包详解
概述
maps 包定义了各种可用于任何类型 map 的函数。该包提供了泛型函数,用于 map 的克隆、复制、比较、删除等操作,简化了 map 的常见操作。
重要说明:
- ✓ Go 1.21+ 引入的泛型工具包
- ✓ 所有函数都是泛型的,适用于任何 map 类型
- ✓ 不支持非自反键(如浮点数 NaN)
包导入
import "maps"
基本使用
1. 克隆 map
package main
import (
"fmt"
"maps"
)
func main() {
m1 := map[string]int{"one": 1, "two": 2}
// 浅克隆
m2 := maps.Clone(m1)
m2["one"] = 100
fmt.Println(m1) // map[one:1 two:2]
fmt.Println(m2) // map[one:100 two:2]
}
2. 复制 map
package main
import (
"fmt"
"maps"
)
func main() {
m1 := map[string]int{"one": 1, "two": 2}
m2 := map[string]int{"three": 3}
// 复制 m2 到 m1
maps.Copy(m1, m2)
fmt.Println(m1) // map[one:1 three:3 two:2]
}
3. 比较 map
package main
import (
"fmt"
"maps"
)
func main() {
m1 := map[string]int{"one": 1, "two": 2}
m2 := map[string]int{"one": 1, "two": 2}
m3 := map[string]int{"one": 1, "three": 3}
fmt.Println(maps.Equal(m1, m2)) // true
fmt.Println(maps.Equal(m1, m3)) // false
}
一、迭代器相关函数
All
定义:
func All[Map ~map[K]V, K comparable, V any](m Map) iter.Seq2[K, V]
说明:
- 功能:返回 map 的键值对迭代器
- 返回值:
iter.Seq2[K, V]- 产生键值对的序列 - 特点:迭代顺序未指定且不保证一致
示例:
package main
import (
"fmt"
"maps"
)
func main() {
m := map[string]int{
"one": 1,
"two": 2,
"three": 3,
}
// 使用 All 迭代
for k, v := range maps.All(m) {
fmt.Printf("%s: %d\n", k, v)
}
}
运行:
$ ./program
one: 1
two: 2
three: 3
Keys
定义:
func Keys[Map ~map[K]V, K comparable, V any](m Map) iter.Seq[K]
说明:
- 功能:返回 map 的键迭代器
- 返回值:
iter.Seq[K]- 产生键的序列 - 特点:迭代顺序未指定
示例:
package main
import (
"fmt"
"maps"
)
func main() {
m := map[string]int{
"one": 1,
"two": 2,
"three": 3,
}
// 只迭代键
for k := range maps.Keys(m) {
fmt.Println(k)
}
}
运行:
$ ./program
one
two
three
Values
定义:
func Values[Map ~map[K]V, K comparable, V any](m Map) iter.Seq[V]
说明:
- 功能:返回 map 的值迭代器
- 返回值:
iter.Seq[V]- 产生值的序列 - 特点:迭代顺序未指定
示例:
package main
import (
"fmt"
"maps"
)
func main() {
m := map[string]int{
"one": 1,
"two": 2,
"three": 3,
}
// 只迭代值
for v := range maps.Values(m) {
fmt.Println(v)
}
}
运行:
$ ./program
1
2
3
二、Map 操作函数
Clone
定义:
func Clone[M ~map[K]V, K comparable, V any](m M) M
说明:
- 功能:返回 m 的浅克隆副本
- 参数:
m- 要克隆的 map - 返回值:新的 map 副本
- 特点:
- 浅克隆:键和值使用普通赋值
- 原 map 和副本共享引用类型的底层数据
示例 1:基本克隆:
package main
import (
"fmt"
"maps"
)
func main() {
m1 := map[string]int{"one": 1, "two": 2}
// 克隆
m2 := maps.Clone(m1)
// 修改副本不影响原 map
m2["one"] = 100
m2["three"] = 3
fmt.Println("m1:", m1) // map[one:1 two:2]
fmt.Println("m2:", m2) // map[one:100 three:3 two:2]
}
示例 2:浅克隆特性:
package main
import (
"fmt"
"maps"
)
func main() {
// 值是指针类型
m1 := map[string]*int{
"one": new(int),
}
*m1["one"] = 1
// 浅克隆
m2 := maps.Clone(m1)
// 修改指针指向的值
*m2["one"] = 100
// 原 map 也受影响(浅克隆)
fmt.Println(*m1["one"]) // 100
fmt.Println(*m2["one"]) // 100
}
Copy
定义:
func Copy[M1 ~map[K]V, M2 ~map[K]V, K comparable, V any](dst M1, src M2)
说明:
- 功能:复制 src 中的所有键值对到 dst
- 参数:
dst- 目标 mapsrc- 源 map
- 特点:
- 如果键已存在,dst 中的值会被覆盖
- 无返回值,直接修改 dst
示例 1:基本复制:
package main
import (
"fmt"
"maps"
)
func main() {
m1 := map[string]int{"one": 1, "two": 2}
m2 := map[string]int{"three": 3, "four": 4}
// 复制 m2 到 m1
maps.Copy(m1, m2)
fmt.Println(m1) // map[four:4 one:1 three:3 two:2]
fmt.Println(m2) // map[four:4 three:3]
}
示例 2:覆盖已存在的键:
package main
import (
"fmt"
"maps"
)
func main() {
m1 := map[string]int{"one": 1, "two": 2}
m2 := map[string]int{"one": 100, "three": 3}
// 复制会覆盖已存在的键
maps.Copy(m1, m2)
fmt.Println(m1) // map[one:100 three:3 two:2]
}
示例 3:切片值的复制:
package main
import (
"fmt"
"maps"
)
func main() {
m1 := map[string][]int{
"one": {1, 2, 3},
}
m2 := map[string][]int{
"one": {100, 200},
"two": {4, 5, 6},
}
// 复制切片值(浅拷贝)
maps.Copy(m1, m2)
fmt.Println(m1) // map[one:[100 200] two:[4 5 6]]
}
DeleteFunc
定义:
func DeleteFunc[M ~map[K]V, K comparable, V any](m M, del func(K, V) bool)
说明:
- 功能:删除满足条件的键值对
- 参数:
m- 要操作的 mapdel- 删除函数,返回 true 时删除该键值对
- 特点:直接修改原 map
示例 1:删除奇数值:
package main
import (
"fmt"
"maps"
)
func main() {
m := map[string]int{
"one": 1,
"two": 2,
"three": 3,
"four": 4,
}
// 删除所有奇数值
maps.DeleteFunc(m, func(k string, v int) bool {
return v%2 != 0
})
fmt.Println(m) // map[four:4 two:2]
}
示例 2:删除特定前缀的键:
package main
import (
"fmt"
"maps"
"strings"
)
func main() {
m := map[string]int{
"temp_one": 1,
"temp_two": 2,
"perm_one": 3,
"perm_two": 4,
}
// 删除所有 temp_ 前缀的键
maps.DeleteFunc(m, func(k string, v int) bool {
return strings.HasPrefix(k, "temp_")
})
fmt.Println(m) // map[perm_one:3 perm_two:4]
}
示例 3:删除空值:
package main
import (
"fmt"
"maps"
)
func main() {
m := map[string]string{
"one": "value1",
"two": "",
"three": "value3",
"four": "",
}
// 删除空值
maps.DeleteFunc(m, func(k string, v string) bool {
return v == ""
})
fmt.Println(m) // map[one:value1 three:value3]
}
三、比较函数
Equal
定义:
func Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool
说明:
- 功能:比较两个 map 是否包含相同的键值对
- 参数:
m1- 第一个 mapm2- 第二个 map
- 返回值:
bool- 相等返回 true - 比较规则:
- 必须有相同的键
- 对应键的值必须相等(使用
==比较)
示例 1:基本比较:
package main
import (
"fmt"
"maps"
)
func main() {
m1 := map[string]int{"one": 1, "two": 2}
m2 := map[string]int{"one": 1, "two": 2}
m3 := map[string]int{"one": 1, "three": 3}
fmt.Println(maps.Equal(m1, m2)) // true
fmt.Println(maps.Equal(m1, m3)) // false
}
示例 2:不同顺序的 map:
package main
import (
"fmt"
"maps"
)
func main() {
// 创建顺序不同
m1 := map[string]int{}
m1["one"] = 1
m1["two"] = 2
m2 := map[string]int{}
m2["two"] = 2
m2["one"] = 1
// Equal 不关心顺序
fmt.Println(maps.Equal(m1, m2)) // true
}
示例 3:空 map 比较:
package main
import (
"fmt"
"maps"
)
func main() {
var m1 map[string]int
m2 := make(map[string]int)
m3 := map[string]int{}
// nil map 和空 map 相等
fmt.Println(maps.Equal(m1, m2)) // true
fmt.Println(maps.Equal(m1, m3)) // true
}
EqualFunc
定义:
func EqualFunc[M1 ~map[K]V1, M2 ~map[K]V2, K comparable, V1, V2 any](m1 M1, m2 M2, eq func(V1, V2) bool) bool
说明:
- 功能:使用自定义比较函数比较两个 map
- 参数:
m1- 第一个 mapm2- 第二个 mapeq- 自定义值比较函数
- 返回值:
bool- 相等返回 true - 特点:
- 键仍然使用
==比较 - 值使用
eq函数比较 - 适用于不同类型值的比较
- 键仍然使用
示例 1:忽略大小写比较:
package main
import (
"fmt"
"maps"
"strings"
)
func main() {
m1 := map[int]string{
1: "one",
10: "Ten",
1000: "THOUSAND",
}
m2 := map[int][]byte{
1: []byte("One"),
10: []byte("Ten"),
1000: []byte("Thousand"),
}
// 忽略大小写比较
eq := maps.EqualFunc(m1, m2, func(v1 string, v2 []byte) bool {
return strings.ToLower(v1) == strings.ToLower(string(v2))
})
fmt.Println(eq) // true
}
示例 2:浮点数近似比较:
package main
import (
"fmt"
"maps"
"math"
)
func main() {
m1 := map[string]float64{
"pi": 3.14159,
"e": 2.71828,
}
m2 := map[string]float64{
"pi": 3.14159265,
"e": 2.71828182,
}
// 近似比较
eq := maps.EqualFunc(m1, m2, func(v1, v2 float64) bool {
return math.Abs(v1-v2) < 0.0001
})
fmt.Println(eq) // true
}
示例 3:结构体比较:
package main
import (
"fmt"
"maps"
)
type Person struct {
Name string
Age int
}
func main() {
m1 := map[int]Person{
1: {Name: "Alice", Age: 30},
2: {Name: "Bob", Age: 25},
}
m2 := map[int]Person{
1: {Name: "Alice", Age: 30},
2: {Name: "Bob", Age: 25},
}
// 使用自定义比较
eq := maps.EqualFunc(m1, m2, func(p1, p2 Person) bool {
return p1.Name == p2.Name && p1.Age == p2.Age
})
fmt.Println(eq) // true
}
四、收集函数
Collect
定义:
func Collect[K comparable, V any](seq iter.Seq2[K, V]) map[K]V
说明:
- 功能:从键值对序列收集到新的 map
- 参数:
seq- 产生键值对的序列 - 返回值:新的 map
示例 1:基本收集:
package main
import (
"fmt"
"maps"
"slices"
)
func main() {
// 从切片创建序列
keys := []int{0, 1, 2, 3}
values := []string{"zero", "one", "two", "three"}
// 使用 Collect 收集
m := maps.Collect(func(yield func(int, string) bool) {
for i := range keys {
if !yield(keys[i], values[i]) {
return
}
}
})
fmt.Println(m) // map[0:zero 1:one 2:two 3:three]
}
示例 2:从 All 收集:
package main
import (
"fmt"
"maps"
)
func main() {
m1 := map[string]int{
"one": 1,
"two": 2,
"three": 3,
}
// 使用 Keys 和 Values 创建新 map
m2 := maps.Collect(maps.All(m1))
fmt.Println(m2) // map[one:1 three:3 two:2]
}
五、插入函数
Insert
定义:
func Insert[Map ~map[K]V, K comparable, V any](m Map, seq iter.Seq2[K, V])
说明:
- 功能:从序列插入键值对到 map
- 参数:
m- 目标 mapseq- 产生键值对的序列
- 特点:如果键已存在,值会被覆盖
示例:
package main
import (
"fmt"
"maps"
)
func main() {
m1 := map[int]string{
0: "zero",
1: "one",
2: "two",
3: "three",
}
// 插入新键值对
maps.Insert(m1, func(yield func(int, string) bool) {
yield(1000, "THOUSAND")
yield(100, "HUNDRED")
})
fmt.Println(m1)
// map[0:zero 1:one 2:two 3:three 100:HUNDRED 1000:THOUSAND]
}
六、典型示例
示例 1:Map 数据处理管道
package main
import (
"fmt"
"maps"
"strings"
)
func main() {
// 原始数据
users := map[string]int{
"alice": 25,
"bob": 30,
"charlie": 35,
"david": 40,
}
// 1. 克隆原始数据
original := maps.Clone(users)
// 2. 过滤:删除年龄小于 30 的用户
maps.DeleteFunc(users, func(k string, v int) bool {
return v < 30
})
// 3. 比较过滤前后
fmt.Println("原始:", original)
fmt.Println("过滤后:", users)
fmt.Println("相等吗?", maps.Equal(original, users))
// 4. 创建大写键的新 map
upperUsers := make(map[string]int)
maps.Insert(upperUsers, func(yield func(string, int) bool) {
for k, v := range users {
if !yield(strings.ToUpper(k), v) {
return
}
}
})
fmt.Println("大写键:", upperUsers)
}
运行:
$ ./program
原始:map[alice:25 bob:30 charlie:35 david:40]
过滤后:map[bob:30 charlie:35 david:40]
相等吗? false
大写键:map[BOB:30 CHARLIE:35 DAVID:40]
示例 2:Map 合并工具
package main
import (
"fmt"
"maps"
)
// Merge 合并多个 map
func Merge[K comparable, V any](maps ...map[K]V) map[K]V {
result := make(map[K]V)
for _, m := range maps {
maps.Copy(result, m)
}
return result
}
// MergeFunc 使用自定义函数合并
func MergeFunc[K comparable, V any](
m1, m2 map[K]V,
mergeFunc func(V, V) V,
) map[K]V {
result := maps.Clone(m1)
for k, v2 := range m2 {
if v1, ok := result[k]; ok {
result[k] = mergeFunc(v1, v2)
} else {
result[k] = v2
}
}
return result
}
func main() {
m1 := map[string]int{"a": 1, "b": 2}
m2 := map[string]int{"b": 3, "c": 4}
m3 := map[string]int{"c": 5, "d": 6}
// 简单合并(后面的覆盖前面的)
merged := Merge(m1, m2, m3)
fmt.Println("合并:", merged)
// map[a:1 b:3 c:5 d:6]
// 自定义合并(值相加)
customMerged := MergeFunc(m1, m2, func(v1, v2 int) int {
return v1 + v2
})
fmt.Println("自定义合并:", customMerged)
// map[a:1 b:5 c:4]
}
示例 3:Map 验证工具
package main
import (
"fmt"
"maps"
)
// HasKey 检查 map 是否包含键
func HasKey[K comparable, V any](m map[K]V, key K) bool {
_, ok := m[key]
return ok
}
// HasValue 检查 map 是否包含值
func HasValue[K comparable, V comparable](m map[K]V, value V) bool {
for _, v := range m {
if v == value {
return true
}
}
return false
}
// FilterKeys 过滤指定的键
func FilterKeys[K comparable, V any](m map[K]V, keys []K) map[K]V {
result := make(map[K]V)
keySet := make(map[K]bool)
for _, k := range keys {
keySet[k] = true
}
for k, v := range m {
if keySet[k] {
result[k] = v
}
}
return result
}
// FilterValues 过滤指定的值
func FilterValues[K comparable, V comparable](m map[K]V, values []V) map[K]V {
result := make(map[K]V)
valueSet := make(map[V]bool)
for _, v := range values {
valueSet[v] = true
}
for k, v := range m {
if valueSet[v] {
result[k] = v
}
}
return result
}
func main() {
users := map[string]int{
"alice": 25,
"bob": 30,
"charlie": 35,
"david": 40,
}
// 检查键
fmt.Println("有 alice 吗?", HasKey(users, "alice")) // true
fmt.Println("有 eve 吗?", HasKey(users, "eve")) // false
// 检查值
fmt.Println("有年龄 30 吗?", HasValue(users, 30)) // true
fmt.Println("有年龄 50 吗?", HasValue(users, 50)) // false
// 过滤键
filtered := FilterKeys(users, []string{"alice", "bob"})
fmt.Println("过滤键:", filtered) // map[alice:25 bob:30]
// 过滤值
filtered = FilterValues(users, []int{30, 35})
fmt.Println("过滤值:", filtered) // map[bob:30 charlie:35]
}
示例 4:使用迭代器
package main
import (
"fmt"
"maps"
"slices"
)
func main() {
m := map[string]int{
"one": 1,
"two": 2,
"three": 3,
"four": 4,
}
// 1. 使用 Keys 迭代
fmt.Print("键:")
for k := range maps.Keys(m) {
fmt.Printf("%s ", k)
}
fmt.Println()
// 2. 使用 Values 迭代
fmt.Print("值:")
for v := range maps.Values(m) {
fmt.Printf("%d ", v)
}
fmt.Println()
// 3. 使用 All 迭代
fmt.Println("键值对:")
for k, v := range maps.All(m) {
fmt.Printf(" %s: %d\n", k, v)
}
// 4. 转换为切片
keys := slices.Collect(maps.Keys(m))
values := slices.Collect(maps.Values(m))
fmt.Println("键切片:", keys)
fmt.Println("值切片:", values)
}
示例 5:Map 转换工具
package main
import (
"fmt"
"maps"
"strconv"
)
// StringKeysToInt 将字符串键转换为整数键
func StringKeysToInt[V any](m map[string]V) map[int]V {
result := make(map[int]V)
maps.Insert(result, func(yield func(int, V) bool) {
for k, v := range m {
if key, err := strconv.Atoi(k); err == nil {
if !yield(key, v) {
return
}
}
}
})
return result
}
// IntKeysToString 将整数键转换为字符串键
func IntKeysToString[V any](m map[int]V) map[string]V {
result := make(map[string]V)
maps.Insert(result, func(yield func(string, V) bool) {
for k, v := range m {
if !yield(strconv.Itoa(k), v) {
return
}
}
})
return result
}
// Invert 反转 map(值必须是可比较的)
func Invert[K comparable, V comparable](m map[K]V) map[V]K {
result := make(map[V]K)
for k, v := range m {
result[v] = k
}
return result
}
func main() {
// 字符串键转整数键
m1 := map[string]int{
"1": 100,
"2": 200,
"3": 300,
}
m2 := StringKeysToInt(m1)
fmt.Println("整数键:", m2) // map[1:100 2:200 3:300]
// 整数键转字符串键
m3 := IntKeysToString(m2)
fmt.Println("字符串键:", m3) // map[1:100 2:200 3:300]
// 反转 map
m4 := map[string]int{"a": 1, "b": 2, "c": 3}
m5 := Invert(m4)
fmt.Println("反转:", m5) // map[1:a 2:b 3:c]
}
七、最佳实践
1. 使用 Clone 保护原 map
// ✓ 好的做法:克隆后修改
func ProcessMap(m map[string]int) map[string]int {
result := maps.Clone(m)
// 修改 result 不会影响 m
result["new"] = 100
return result
}
// ✗ 不好的做法:直接修改参数
func ProcessMap(m map[string]int) {
m["new"] = 100 // 修改了原 map
}
2. 使用 Copy 合并 map
// ✓ 好的做法:使用 Copy
func Merge(m1, m2 map[string]int) map[string]int {
result := maps.Clone(m1)
maps.Copy(result, m2)
return result
}
3. 使用 DeleteFunc 过滤
// ✓ 好的做法:使用 DeleteFunc
func FilterEven(m map[string]int) {
maps.DeleteFunc(m, func(k string, v int) bool {
return v%2 != 0
})
}
4. 使用 Equal 比较
// ✓ 好的做法:使用 Equal
if maps.Equal(m1, m2) {
fmt.Println("相等")
}
// ✗ 不好的做法:手动比较
// 需要写很多代码且容易出错
5. 使用迭代器函数
// ✓ 好的做法:使用 Keys 迭代
for k := range maps.Keys(m) {
fmt.Println(k)
}
// ✓ 好的做法:使用 Values 迭代
for v := range maps.Values(m) {
fmt.Println(v)
}
// ✓ 好的做法:使用 All 迭代
for k, v := range maps.All(m) {
fmt.Printf("%s: %d\n", k, v)
}
八、与其他包配合
1. 与 slices 包配合
import (
"maps"
"slices"
)
// 将 map 的键转换为切片
keys := slices.Collect(maps.Keys(m))
// 将 map 的值转换为切片
values := slices.Collect(maps.Values(m))
// 将 map 的键值对转换为切片
pairs := slices.Collect(maps.All(m))
2. 与 iter 包配合
import (
"iter"
"maps"
)
// 使用 iter 包的函数处理 map
func FilterMap[K comparable, V any](
m map[K]V,
filter func(K, V) bool,
) map[K]V {
result := make(map[K]V)
maps.Insert(result, func(yield func(K, V) bool) {
for k, v := range maps.All(m) {
if filter(k, v) {
if !yield(k, v) {
return
}
}
}
})
return result
}
九、快速参考
函数总览
| 函数名 | 参数 | 返回值 | 描述 |
|---|---|---|---|
All | m Map | iter.Seq2[K, V] | 键值对迭代器 |
Clone | m M | M | 浅克隆 |
Collect | seq iter.Seq2[K, V] | map[K]V | 收集到 map |
Copy | dst M1, src M2 | 无 | 复制 map |
DeleteFunc | m M, del func(K, V) bool | 无 | 条件删除 |
Equal | m1 M1, m2 M2 | bool | 比较相等 |
EqualFunc | m1 M1, m2 M2, eq func | bool | 自定义比较 |
Insert | m Map, seq iter.Seq2[K, V] | 无 | 插入键值对 |
Keys | m Map | iter.Seq[K] | 键迭代器 |
Values | m Map | iter.Seq[V] | 值迭代器 |
类型约束
| 类型参数 | 约束 | 说明 |
|---|---|---|
K | comparable | 键类型,必须可比较 |
V | any | 值类型,任意类型 |
M | ~map[K]V | map 类型或其底层类型 |
Map | ~map[K]V | map 类型或其底层类型 |
十、注意事项
1. 浅克隆特性
// Clone 是浅克隆
m1 := map[string]*int{"one": new(int)}
*m1["one"] = 1
m2 := maps.Clone(m1)
*m2["one"] = 100
// m1 也受影响
fmt.Println(*m1["one"]) // 100
2. 迭代顺序不保证
// 不保证顺序,不要依赖
for k := range maps.Keys(m) {
// 顺序可能每次不同
}
3. nil map 处理
// Clone nil map 返回 nil
var m map[string]int
m2 := maps.Clone(m) // nil
// Copy 到 nil map 会 panic
var dst map[string]int
src := map[string]int{"one": 1}
maps.Copy(dst, src) // panic!
// 必须先初始化
dst = make(map[string]int)
maps.Copy(dst, src) // OK
4. 性能考虑
// ✓ 好的做法:预先分配容量
m := make(map[string]int, len(src))
maps.Copy(m, src)
// 对于大 map,考虑分批处理
最后更新: 2026-04-05
Go 版本: Go 1.21+
包文档: https://pkg.go.dev/maps