本文永久链接 – https://tonybai.com/2026/11/13/go-ordered-map-sorted-collection-proposal-60630

大家好,我是Tony Bai。

【导读】

Go 的 map 很好用,但有一个众所周知的限制:它不保证遍历顺序。

如果业务需要按照 Key 排序遍历、获取最小或最大 Key、查询某个区间内的元素,开发者往往只能自己维护排序逻辑,或者引入第三方有序集合实现。

这个问题终于有望得到官方生态层面的解决。

近日,Go 技术负责人 Austin Clements 将 golang/go#60630 中的有序 Map/Set 提案列为 Accepted。这意味着 Go 的实验性集合库 golang.org/x/exp/collection/sorted 又向前迈出了重要一步。

继泛型 Set 提案被接受之后,Go 的 Collection API 正在逐渐拼出一套更加完整的集合体系。

【文章要点】

  • 原生 map 的遍历顺序不确定,不应依赖其输出顺序。
  • 有序 Map 不只是排序遍历,还涉及最小值、最大值、范围查询和反向遍历。
  • 比较函数让 Key 不必局限于内置有序类型。
  • Go 1.23 引入的函数迭代器为这类集合 API 提供了重要基础。
  • x/exp 中的实验性集合库不等于 Go 标准库已经正式加入对应 API。
  • Go 正在探索哈希集合、排序集合、堆等更完整的泛型集合体系。

【Go, Rust & AI 精进营】一年仅一次 · 双 11 限时特惠,长按下方图片扫码,直降入营:


一个困扰 Go 开发者多年的问题

先来看一个非常普通的 Go 程序:

package main  import "fmt"  func main() {  m := map[int]string{  30: "Go",  10: "Rust",  20: "C",  }   for k, v := range m {  fmt.Println(k, v)  } } 

如果我们希望按照 Key 从小到大的顺序输出,怎么办?

最直接的办法是:

keys := make([]int, 0, len(m)) for k := range m {  keys = append(keys, k) }  slices.Sort(keys)  for _, k := range keys {  fmt.Println(k, m[k]) } 

这当然没有问题。

但如果我们的需求不仅仅是排序输出呢?

例如:

  • 获取 Key 最小或最大的元素。
  • 按照 Key 的范围查询数据。
  • 从某个 Key 开始向后遍历。
  • 反向遍历整个集合。
  • 查询某个 Key 在有序集合中的排名。
  • 获取指定排名位置上的元素。

这时,普通 map 就不那么合适了。

开发者通常需要自己维护一套数据结构,例如红黑树、AVL 树或者 B-Tree。

Java 有 TreeMap、TreeSet,Rust 有 BTreeMap、BTreeSet,C++ 也有基于树结构的有序关联容器。

而 Go 的标准库一直没有提供对应的通用有序 Map/Set。

这也是 #60630 提案最初提出的原因。

它希望为 Go 开发者提供一套基于 Key 顺序的泛型集合。

从 2023 到 2026:一个提案的漫长演进

这个提案最初由开发者 leaxoy 于 2023 年 6 月 6 日提交。

当时的想法非常直接:在 Go 标准库中增加 ordered.Map 和 ordered.Set。

但提案并没有很快得到推进。

一个重要原因是,Go 当时还没有用户自定义迭代器机制。

Ian Lance Taylor 在提案早期就指出,在设计通用容器之前,需要先解决如何遍历容器中所有元素的问题。

这实际上把问题引向了 Go 语言迭代器设计。

随后,Go 1.23 在 2024 年正式引入了 Range over Function Types。

开发者可以使用 iter.Seq 和 iter.Seq2 表达迭代器,并直接通过 for range 遍历。

例如:

func All() iter.Seq2[int, string] {  return func(yield func(int, string) bool) {  // 逐个产生 Key、Value  } } 

有了这项语言能力,集合 API 的设计空间也随之打开。

2024 年,提案作者和 Go 核心开发者 Alan Donovan 等人继续讨论 API 设计,逐渐将原先返回切片的遍历方式调整为迭代器。

2026 年,Go 团队进一步将其纳入更完整的 Collection API 规划。

10 月,提案更新了 API,增加了 Set 类型,并逐步完善范围操作。

10 月 29 日,Austin Clements 将提案状态调整为 Likely Accept。

11 月 12 日,Austin Clements 最终给出了正式接受意见:

No change in consensus, so accepted.

这意味着该提案正式获得接受,接下来将进入具体实现和完善阶段。

一个从 2023 年提出、经历了三年多讨论的提案,终于迎来了重要节点。

sorted Map/Set 到底能做什么?

提案最终的设计位于:

golang.org/x/exp/collection/sorted

它提供两种核心数据结构:

type Map[K, V any] struct {  // private fields }  type Set[T any] struct {  // private fields } 

与 Go 原生 map 不同,sorted.Map 的 Key 按照构造时提供的比较函数排序。

创建一个 Map:

m := sorted.NewMap[int, string](  cmp.Compare[int], ) 

这里有一个值得关注的设计:

Key 不需要满足 cmp.Ordered,只需要提供比较函数。

这意味着,即使 Key 是一个复杂的自定义结构体,也可以定义自己的排序规则。

例如,我们可以按照用户的年龄排序:

type User struct {  Name string  Age int }  users := sorted.NewMap[User, string](  func(a, b User) int {  return cmp.Compare(a.Age, b.Age)  }, ) 

当然,实际使用时还需要考虑比较函数是否满足严格一致的排序关系,尤其是比较结果为 0 时,两个 Key 会被视为排序意义上的等价元素。

按顺序遍历,不再需要手动排序

提案设计了 All() 方法:

for k, v := range m.All() {  fmt.Println(k, v) } 

All() 返回 iter.Seq2[K, V],按照 Key 从小到大的顺序遍历。

如果希望反向遍历:

for k, v := range m.Backward() {  fmt.Println(k, v) } 

同时还提供:

m.Keys() m.Values() 

分别按照 Key 的顺序返回 Key 和 Value 迭代器。

这与 Go 1.23 之后的迭代器设计保持一致。

获取最大值、最小值

有序集合的一个重要价值,就是能够方便地访问边界元素。

例如:

k, v, ok := m.Min() if ok {  fmt.Println(k, v) }  k, v, ok = m.Max() if ok {  fmt.Println(k, v) } 

不需要先遍历整个 Map,也不需要自己维护一个额外的最小值或最大值变量。

范围查询:真正体现有序 Map 价值的能力

这是我认为最值得关注的功能之一。

假设我们有一个按照时间戳排序的 Map,希望查询时间戳在 100 到 200 之间的所有数据。

有序集合可以通过 Span 表达范围。

提案设计了:

type Span[K any] struct {  // private fields }  func From[K any](lo K) Span[K] func To[K any](hi K) Span[K] func Above[K any](lo K) Span[K] func Below[K any](hi K) Span[K] 

其中:

  • From:包含下界。
  • To:包含上界。
  • Above:不包含下界。
  • Below:不包含上界。

最终接受的 API 还增加了 Map.Span() 和 Set.Span(),用于取得整个集合对应的范围视图。

概念上,查询可以表达为:

span := m.Span().From(100).To(200)  for k, v := range span.All() {  fmt.Println(k, v) } 

这里展示的是提案 API 的使用意图,具体签名和调用形式应以最终合入的实现为准。

范围查询让有序 Map 不再只是一个能够排序遍历的 Map,而是一个真正支持有序索引操作的数据结构。

不只是遍历,还能访问排名

提案还提供了 Index 和 Nth:

index := m.Index(key)  k, v := m.Nth(10) 

前者查询某个 Key 在有序集合中的位置,后者获取指定位置上的 Key 和 Value。

这让它具备了一些有序索引结构的能力。

例如,排行榜、时间窗口、Top-N 查询等场景,都可能从中受益。

它和普通 map 有什么不同?

需要强调的是,sorted.Map 并不是要取代 Go 原生 map。

二者解决的是不同问题。

特性 原生 map sorted.Map
泛型 是 是
遍历顺序 不保证 按 Key 排序
自定义比较函数 不支持 支持
最小/最大 Key 需要自行处理 内置 API
范围查询 需要自行实现 支持 Span
反向遍历 需要自行处理 支持
排名与位置访问 需要自行实现 支持

性能方面也存在明显区别。

原生 map 基于哈希表,查找和更新通常具有 O(1) 的平均时间复杂度。

有序 Map 则需要维护 Key 的排序关系。

当前提案关联的实现采用平衡二叉树,因此查找、插入和删除的典型复杂度为 O(log N)。

但它能够更自然地支持范围查询、顺序遍历和排名操作。

所以,选择哪种数据结构,取决于业务需求。

如果只是普通 Key-Value 存储,原生 map 仍然是首选。

如果需要有序遍历、范围查询、排名和边界访问,那么有序 Map 就更合适。

Go Collection API 正在形成更大的体系

如果只把 #60630 看成一个有序 Map 提案,可能低估了它的意义。

实际上,Go 团队正在推进一项更大的工作:为 Go 构建一套通用的泛型集合 API。

另一个值得关注的提案是:

#69230:golang.org/x/exp/collection/set

它提出了一个通用的泛型 Set 类型,目标是为 Go API 提供一种规范的集合表示。

该提案已于 2026 年 11 月 4 日被接受。

这两个提案的侧重点并不相同:

  • collection/set:基于 Go 原生 Map 的哈希集合。
  • collection/sorted:基于 Key 顺序的有序 Map 和 Set。

此外,Go 团队还在推进其他集合类型,例如:

  • collection/hashed:基于哈希的泛型 Map/Set。
  • collection/heap:泛型堆。

可以将目前的集合体系理解为:

更重要的是,这些 API 并不是直接全部加入 Go 标准库。

Go 团队希望先将它们放在 golang.org/x/exp 中,经过一段时间的社区使用和反馈,再考虑未来是否进入标准库。

这是一种相对稳妥的标准库演进方式。

写在最后

从泛型,到 Range over Function Types,到泛型方法,再到如今的泛型 Set、有序 Map/Set,Go 的集合 API 正在逐渐补齐。

回头看 #60630 这个提案,最有意思的地方其实不是有序 Map 本身,而是它的设计过程。

2023 年,提案提出时,Go 还没有成熟的用户自定义迭代器机制。

2024 年,Go 1.23 引入迭代器,让集合 API 有了更自然的遍历表达方式。

2026 年,Go 团队开始将这些能力整合到更完整的 Collection API 体系中。

这说明语言特性与标准库 API 并不是孤立演进的。

一个看似简单的集合类型,背后可能涉及泛型、迭代器、比较函数、API 一致性以及标准库长期兼容性等一系列问题。

当然,Accepted 不等于已经进入标准库,更不等于所有实现细节已经尘埃落定。

但对于长期使用 Go 的开发者来说,这无疑是一个值得关注的进展。

以后,当我们需要一个有序 Map 时,或许终于不必再从 GitHub 上搜索各种 TreeMap 实现了。

Go 的 Collection API,正在成形。

原始提案及最终 Accepted 记录:golang/go#60630 - https://github.com/golang/go/issues/60630


还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:

  • 抛弃臃肿框架,回归“驾驭工程 (Harness Engineering)”的第一性原理
  • 用 Go 语言手写 ReAct 循环、并发拦截与上下文压缩引擎等,复刻极简OpenClaw
  • 构建坚不可摧的 Safety Middleware 与飞书人工审批防线
  • 在底层实现 Token 成本审计、链路追踪与自动化跑分评估
  • 从“调包侠”进化为掌控大模型边界的“AI 操作系统架构师”

扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。