首页 / Go 语言入门教程 / maps 标准库与 cmp(1.21+)

Go 语言入门教程

maps 标准库与 cmp(1.21+)

本教程共 80 篇 · 第 35 篇 · 更新于 2026-07-27 · 约 6 分钟阅读

GoGo 入门教程mapscmp标准库CloneCompare

35. maps 标准库与 cmp(1.21+)

本节目标:学会用 Go 1.21 的 maps 标准库克隆 map、提取 key/value,以及用 cmp 包做类型安全的比较。

maps 标准库

和 slices 包一样,maps 也是 Go 1.21 引入的标准库,把以前手写的 map 操作收编了。

Note

maps 包需要 Go 1.21 及以上版本。它大量使用了泛型,所以能适配各种 key 和 value 类型。

maps.Clone:克隆 map

Clone 创建一个独立的 map 副本:

package main

import (
    "fmt"
    "maps"
)

func main() {
    original := map[string]int{
        "apple":  5,
        "banana": 3,
    }

    copied := maps.Clone(original)
    copied["apple"] = 100

    fmt.Println(original["apple"]) // 5    原map没变
    fmt.Println(copied["apple"])   // 100  副本独立
}

以前克隆 map 得自己写循环:

// 老办法
copied := make(map[string]int, len(original))
for k, v := range original {
    copied[k] = v
}

现在一行 maps.Clone(original) 搞定。

Warning

Clone 是浅拷贝。如果 value 是引用类型(切片、map、指针),副本和原件会共享底层数据。

maps.Keys 和 maps.Values:提取键和值

Keys 返回所有 key 组成的切片,Values 返回所有 value 组成的切片:

package main

import (
    "fmt"
    "maps"
    "slices"
)

func main() {
    m := map[string]int{
        "apple":  5,
        "banana": 3,
        "cherry": 8,
    }

    keys := maps.Keys(m)
    values := maps.Values(m)

    // Go 1.23+ 返回迭代器,用 slices.Sorted 收集并排序
    // Go 1.21-1.22 直接返回切片,用 slices.Sort 排序
    fmt.Println(slices.Sorted(keys))   // [apple banana cherry]
    fmt.Println(slices.Sorted(values)) // [3 5 8]
}
Tip

在 Go 1.23 中,maps.Keysmaps.Values 返回的是迭代器(iterator)。可以用 slices.Collect 收集成切片,或用 slices.Sorted 收集并排序。在 Go 1.21-1.22 中,这两个函数直接返回切片,排序用 slices.Sort

maps.Copy:批量复制

Copy 把 src 的所有键值对复制到 dst,已存在的 key 会被覆盖:

package main

import (
    "fmt"
    "maps"
)

func main() {
    dst := map[string]int{"a": 1, "b": 2}
    src := map[string]int{"b": 20, "c": 30}

    maps.Copy(dst, src)
    fmt.Println(dst) // map[a:1 b:20 c:30]
}

适合用来做配置合并:把多个小 map 合并成一个大 map。

maps.Equal:比较两个 map

m1 := map[string]int{"a": 1, "b": 2}
m2 := map[string]int{"a": 1, "b": 2}
m3 := map[string]int{"a": 1, "b": 3}

fmt.Println(maps.Equal(m1, m2)) // true
fmt.Println(maps.Equal(m1, m3)) // false

maps.EqualFunc:自定义比较

如果 value 是不可比较的类型(比如切片),用 EqualFunc 传入自定义比较函数:

m1 := map[string][]int{"a": {1, 2}}
m2 := map[string][]int{"a": {1, 2}}

fmt.Println(maps.EqualFunc(m1, m2, slices.Equal)) // true

maps.DeleteFunc:条件删除

DeleteFunc 删除所有满足条件的键值对:

package main

import (
    "fmt"
    "maps"
)

func main() {
    m := map[string]int{
        "apple":  5,
        "banana": 3,
        "cherry": 8,
        "date":   2,
    }

    // 删除所有值小于5的
    maps.DeleteFunc(m, func(k string, v int) bool {
        return v < 5
    })

    fmt.Println(m) // map[apple:5 cherry:8]
}

cmp 包:类型安全的比较

cmp 包也是 Go 1.21 新增的,提供了两个核心东西。

cmp.Ordered 约束

cmp.Ordered 是一个类型约束,包含所有支持排序的类型:

// cmp.Ordered 包含的类型
// ~int ~int8 ~int16 ~int32 ~int64
// ~uint ~uint8 ~uint16 ~uint32 ~uint64 ~uintptr
// ~float32 ~float64
// ~string

你在写泛型函数时,如果需要对参数做排序或比较,用 cmp.Ordered 做约束就行。

cmp.Compare 函数

Compare 返回两个值的比较结果:

  • 返回负数:a < b
  • 返回 0:a == b
  • 返回正数:a > b
package main

import (
    "cmp"
    "fmt"
)

func main() {
    fmt.Println(cmp.Compare(1, 2))    // -1
    fmt.Println(cmp.Compare(2, 2))    // 0
    fmt.Println(cmp.Compare(3, 2))    // 1

    fmt.Println(cmp.Compare("apple", "banana")) // -1  字符串也能比
}

在写排序比较函数时特别好用:

type Person struct {
    Name string
    Age  int
}

people := []Person{{"Bob", 25}, {"Alice", 30}}

slices.SortFunc(people, func(a, b Person) int {
    return cmp.Compare(a.Age, b.Age) // 按年龄排序
})

cmp.Less 函数

Less 就是 Compare(a, b) < 0 的简写:

fmt.Println(cmp.Less(1, 2))  // true
fmt.Println(cmp.Less(2, 1))  // false

小结

Go 1.21 的 maps 和 cmp 包让 map 操作和比较操作现代化了:

  • maps.Clone 一行克隆 map
  • maps.Keys / maps.Values 提取键值
  • maps.Copy 合并 map
  • maps.Equal / EqualFunc 比较 map
  • cmp.Compare 类型安全的比较
  • cmp.Ordered 排序类型约束

下一部分进入指针和结构体,搞懂 Go 的值语义和引用语义。