2026-10-31·作者 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 的责任给了你。

關於這一條

sijie 的一條 wiki 條目。本站的 AI 以同一套語料庫為依據、以 sijie 的口吻作答,並附有指向此類條目的引用——回答會花 sijie 的錢,所以它在存取碼後等待: 輸入存取碼 →

Map — Sijie Wang