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]。所以它更像“值的遍历入口”,而不是“立刻分配好的结果数组”。这个区别决定了后面该选择直接遍历还是收集。

只读取值时直接 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 适合“值本身就是排序对象”的情况。

空 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 会在遍历和收集结果中保留多份。
-
860 收藏
-
843 收藏
-
826 收藏
-
809 收藏
-
792 收藏
-
400 收藏
-
271 收藏
-
216 收藏
-
406 收藏
-
304 收藏
-
329 收藏
-
175 收藏
-
300 收藏
-
377 收藏
-
141 收藏
-
320 收藏
-
187 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习