2026-10-31·by Sijie Wang

Map

Map:内置哈希表,引用语义

动机:最常用的结构,语言内置。 Go 的 map 不是库里的数据结构,是语言自己提供的哈希表,字面量直接写。正因为是内置的,它的语义值得单开一节:其中三条会让从别的语言过来的人意外。

引用语义:变量本身就是指针。 map 变量指向底层的哈希表,赋值、传参拷的都是指针,不拷数据,所以函数里往收到的 map 写东西,外面的 map 也看得见。跟 slice 一个道理,跟数组正好反过来,数组是拷内容的。

并发写是 fatal,不是异常。 两个 goroutine 同时写一个 map,runtime 检测到就抛 concurrent map writes,整个进程死掉,没得 catch。跟 Java 对比能看清狠在哪儿:Java 的 HashMap 是 fail-fast,迭代时检测到就抛 ConcurrentModificationException,那是个异常,能 catch,而且有些并发写它还不抛,是静默丢数据,老版本扩容甚至能死循环。Go 选了更狠的一边:检测到就杀进程。所以跨 goroutine 共享的 map,一定是上层加锁,或者换 sync.Map。

迭代顺序随机化是故意的。 每次 range 随机挑一个起始桶、桶里随机挑一个起始格子,顺序每次都不一样。要注意随机的是什么:只是迭代起点,不是存储。键放哪个桶还是哈希说了算,存的时候是确定的。目的是不让你依赖顺序:如果顺序是固定的,依赖它的代码今天测试全过,哪天哈希种子一变、扩容一次、Go 版本一升,顺序变了,程序就悄悄错了。随机化让依赖顺序的代码在开发时就时灵时不灵,逼你用老实办法:先把 key 取出来 sort 一遍,再按排好的顺序取。

生产注记。 两条规矩:不依赖 map 迭代顺序,不跨 goroutine 裸共享 map。两条同源:map 是共享的可变表,Go 给了这张表的速度,也把 guard 的责任给了你。

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →

Map — Sijie Wang