
考虑以下问题。你有一个包含数百万个字符串的大型数据集。你需要将这些字符串映射到 8 字节整数( uint64 )。这些整数已给出。
如果你用 Go 语言开发,标准解决方案是创建一个 map。构建过程很简单,类似于下面的循环。
m := make ( map [ string ] uint64 , N ) for i , k := range keys { m [ k ] = values [ i ] }缺点之一是,该映射表每个条目可能占用超过 50 字节。m := make ( map [ string ] uint64 , N ) for i , k := range keys { m [ k ] = values [ i ] }
在某些重要场景下,我们可能会遇到以下情况:映射表很大(包含一百万条或更多条目),无需动态修改(它是不可变的),并且所有查询的键都在集合中。在这种情况下,可以将内存使用量降低到几乎与键的大小相当,即每个条目大约 8 字节。一种快速的方法是使用二进制融合过滤器。
我用 Go 语言实现了一个名为constmap 的库,它提供了一个使用二进制 fuse 过滤器将字符串映射到uint64值的不可变映射。这种数据结构非常适合在构建时拥有固定键值对,并且之后需要快速、内存高效的查找的情况。你甚至可以只构建一次映射并将其保存到磁盘,这样每次需要使用时就无需重新构建映射。
使用方法同样简单。
package main import ( "fmt" "log" "github.com/lemire/constmap" ) func main () { keys := [] string { "apple" , "banana" , "cherry" } values := [] uint64 { 100 , 200 , 300 } cm , err := constmap . New ( keys , values ) if err != nil { log . Fatal ( err ) } fmt . Println ( cm . Map ( "banana" )) // 200 }构造时间较长(正如任何紧凑数据结构所预期的那样),但查找操作针对速度进行了优化。我在我的 Apple M4 Max 处理器上运行了基准测试,以比较 constmap 查找与 Go 内置查找的性能。package main import ( "fmt" "log" "github.com/lemire/constmap" ) func main () { keys := [] string { "apple" , "banana" , "cherry" } values := [] uint64 { 100 , 200 , 300 } cm , err := constmap . New ( keys , values ) if err != nil { log . Fatal ( err ) } fmt . Println ( cm . Map ( "banana" )) // 200 }
map[string]uint64 。该测试使用了 100 万个键。
| 数据结构 | 查找时间 | 内存使用情况 |
|---|---|---|
| ConstMap | 7.4 纳秒/操作 | 9 字节/密钥 |
| 前往地图 | 20 纳秒/操作 | 56 字节/密钥 |
ConstMap 的查找速度比 Go 的标准 map 快近 3 倍!而且内存使用量减少了 6 倍。
源代码该实现可在 GitHub 上找到: github.com/lemire/constmap 。
原文: https://lemire.me/blog/2026/03/29/a-fast-immutable-map-in-go/