登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go maps.Values 怎样提取 map 值集合

来源:17golang原创

时间:2026-10-20 05:00:17 301浏览 收藏

如果你的代码已经使用 Go 1.23 或更高版本,提取 map 的值集合可以直接写成 maps.Values(m)。它返回的是一个值迭代器 iter.Seq[V],可以直接用 range 遍历,也可以交给 slices.Collect 收集成切片。

官方地址:https://pkg.go.dev/maps

我第一次把它用在配置快照导出时,最容易误判的地方是“值集合”听起来像一个已经排好序的 []V。实际情况正相反:maps.Values 只负责提供值,不承诺迭代顺序。下面按使用压力拆开这个边界。

maps.Values 返回的不是切片

标准库签名可以概括为:

func Values[Map ~map[K]V, K comparable, V any](m Map) iter.Seq[V]

它有三个关键信息:输入可以是底层类型为 map 的自定义类型;值类型可以是任意类型;返回值是 iter.Seq[V]。所以它更像“值的遍历入口”,而不是“立刻分配好的结果数组”。这个区别决定了后面该选择直接遍历还是收集。

Go maps.Values 从 map 输入到值迭代器再到 range 消费的静态关系说明图
图1:maps.Values 的静态职责关系说明图,展示 map、值迭代器和消费端之间的边界;这是原创说明图,不是运行截图。

只读取值时直接 range

如果目标只是统计、过滤、拼接或把值交给一个处理函数,没有必要先创建切片。直接遍历迭代器就够了:

package main

import (
	"fmt"
	"maps"
)

type Server struct {
	Name   string
	Online bool
}

func main() {
	servers := map[string]Server{
		"api":   {Name: "api", Online: true},
		"worker": {Name: "worker", Online: false},
	}

	count := 0
	for server := range maps.Values(servers) {
		// 只消费值,不关心 key,也不创建中间切片。
		if server.Online {
			count++
		}
	}

	fmt.Println("online:", count)
}

这里的 server 是值类型 Server,不是键值对。对只关心值的场景来说,这比先写一轮普通 for key, value := range m 再忽略 key 更直接。

需要 []V 时用 slices.Collect

当下游 API 明确要求切片,或者你要做切片索引、传递给其他切片函数,可以把迭代器收集起来:

package main

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

func main() {
	prices := map[string]int{
		"book":  39,
		"mouse": 89,
		"cable": 19,
	}

	values := slices.Collect(maps.Values(prices))
	// Collect 把 iter.Seq[int] 物化为 []int,方便传给切片 API。
	fmt.Println(values)
}

slices.Collect 只改变容器形态,不会替你建立业务顺序。它得到的切片顺序仍然继承 map 遍历的不确定性;如果下游要稳定序列化、生成测试快照或展示列表,就还要单独排序。

需要稳定顺序时显式排序

对可排序的值,可以直接使用 slices.Sorted 消费 maps.Values:

package main

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

func main() {
	labels := map[int]string{
		1:    "one",
		10:   "Ten",
		1000: "THOUSAND",
	}

	ordered := slices.Sorted(maps.Values(labels))
	// Sorted 明确建立字符串升序,避免把 map 的遍历顺序当成业务顺序。
	fmt.Println(ordered)
}

如果你的业务顺序来自 key,而不是 value 本身,那么“只取值再排序”可能丢失排序依据。此时应改为先收集键,或在普通 range 中同时保留 key 和 value,再按业务规则组织结果。maps.Values 适合“值本身就是排序对象”的情况。

Go maps.Values 的无序值集合与显式排序结果之间的静态关系说明图
图2:值集合的顺序边界说明图,展示无序迭代、切片收集和显式排序之间的职责关系;这是原创说明图,不是执行证据。

空 map、nil map 与重复值怎么理解

maps.Values 不会把 map 的 key 重新编码到结果里,因此要记住三个边界:

场景直接遍历收集后结果需要留意
nil map不会遍历任何值得到空集合语义不要把它当成错误
空 map不会进入循环体得到空切片下游是否区分 nil 和空切片要看接口约定
不同 key 对应相同 value会遍历多次结果保留重复值Values 不负责去重
需要稳定顺序顺序不可依赖仍不可依赖显式排序或保留 key

尤其是重复值:map 的 key 唯一,不代表 value 唯一。如果目标是得到去重后的值集合,需要额外使用一个集合结构记录已见值;这已经超出 maps.Values 本身的职责。

Go 1.22 及更早版本怎么办

maps.Values 是 Go 1.23 起的标准库能力。如果项目暂时不能升级,可以用普通 range 写出等价的值收集逻辑:

values := make([]string, 0, len(users))
for _, user := range users {
	// 兼容旧版本 Go:忽略 key,只把 value 追加到切片。
	values = append(values, user.Name)
}

这不是两个 API 的结果顺序差异,而是同一个 map 顺序边界的两种写法。升级到 Go 1.23 后,maps.Values 主要带来的是表达力和与迭代器生态的组合能力,不会自动把 map 变成有序容器。

我会怎样选择

  • 只消费值:使用 for value := range maps.Values(m),避免无意义的中间切片。
  • 下游明确要 []V:使用 slices.Collect(maps.Values(m))。
  • 需要稳定的值排序:使用 slices.Sorted(maps.Values(m)),或收集后按业务比较器排序。
  • 排序依据是 key、需要去重或要保留键值关系:不要只提取 values,保留普通 range 的键值对。
  • 项目低于 Go 1.23:继续使用普通 range,不要为了一个辅助函数强行改变工具链。

一句话总结:maps.Values 解决的是“怎样把 map 的值暴露为可遍历序列”,不解决切片物化、排序、去重或业务顺序。先判断下游真正需要什么,再决定直接遍历、收集,还是保留 key。

相关问题

  • maps.Values 会返回稳定顺序吗?不会,官方文档明确说明顺序不指定,也不保证多次调用一致。
  • 怎么把 maps.Values 变成切片?Go 1.23+ 使用 slices.Collect(maps.Values(m))。
  • 怎么保证输出顺序?让值满足排序约束后使用 slices.Sorted,或者保留 key 按业务规则排序。
  • maps.Values 会去重吗?不会,不同 key 的相同 value 会在遍历和收集结果中保留多份。
声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>