go中哈希计算由哈希类型、写入方式和取值方法共同决定;sha256.new()必须用sum(nil)获取结果,不可用sum32()(该方法不存在于crypto/sha256);fnv.new32a()则必须用sum32(),sum(nil)语义错误;文件哈希应流式处理避免内存爆炸;结构体/切片作map键时哈希由运行时隐式生成,但含指针、切片、map等字段的类型不可作键。

Go 里哈希计算不是“传参决定结果”,而是由**哈希类型 + 写入方式 + 取值方法**三者共同决定;用错 Sum() 或混用 Sum32() 和 Sum(nil),十次有九次算出空值或 panic。
crypto/sha256.New() 后必须用 Sum(nil),不能用 Sum32()
sha256 实现的是 hash.Hash 接口,它没有 Sum32() 方法——这个函数只存在于 hash/fnv 和 hash/crc32 等非加密哈希包里。调用 sha256.New().Sum32() 会直接编译失败。
-
sha256.New()返回一个可复用的哈希实例,支持多次Write() - 必须用
h.Sum(nil)获取最终哈希字节切片(nil表示新建底层数组) - 想转十六进制,就
hex.EncodeToString(h.Sum(nil)),别漏掉hex包 - 若只算一次小字符串,可用
sha256.Sum([]byte(s)).Sum(nil),但注意返回的是结构体,不是接口
hash/fnv.New32a() 必须用 Sum32(),Sum(nil) 虽不报错但语义错
fnv.New32a() 返回的也是 hash.Hash 接口,但它额外实现了 Sum32() 方法。这里容易踩坑:
-
h.Sum(nil)每次都新建一个[]byte,返回长度为 4 的切片,内容是哈希值的字节序(小端) -
h.Sum32()直接返回uint32,快、无分配,适合做 map 键或分片路由 - 别写成
h.Sum(nil)[0]去取第一个字节——那是低字节,不是整数哈希值 - 如果需要 64 位,就用
fnv.New64a()+Sum64(),别强行左移拼接
对文件哈希时,Write 的数据来源决定内存是否爆炸
哈希对象本身不关心你给什么,但 Write() 的参数类型和来源影响巨大:
- 错误做法:
data, _ := ioutil.ReadFile("big.log"); h.Write(data)→ 整个文件进内存 - 正确做法:
f, _ := os.Open("big.log"); io.Copy(h, f)→ 流式读,内存恒定在几 KB - 手动分块也行:
buf := make([]byte, 64*1024); for { n, _ := f.Read(buf); h.Write(buf[:n]) } - 注意:
io.Copy要求目标实现io.Writer,而所有hash.Hash都满足——这点常被忽略,导致自己写循环
结构体/切片作键时,哈希值不是“算出来”的,是编译器隐式生成的
Go 不允许你给结构体或切片显式传参去“算哈希”,map 键的哈希由运行时自动处理,但有硬限制:
- 含
[]int、map[string]int、func()、chan int的结构体,**根本不能做 map 键**,编译报错,不是哈希不准 - 切片永远不可哈希:底层指针每次运行地址不同,
[]byte("a")和[]byte("a")哈希值不等 - 想用切片内容当键?得自己遍历算:
for _, v := range s { hash = hash*31 + uint64(v) } - 结构体含
*string也不稳:nil 指针和指向空字符串的指针哈希不同,建议用值类型字段
真正难的不是怎么调函数,而是判断该不该哈希、该用哪个哈希、以及哈希值到底要用来干什么——校验文件就用 crypto/sha256,路由分片就用 hash/fnv,而结构体当键这事,多数时候该换设计,而不是硬算哈希。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











