golang 对象hash原理
map里面的key的hash是怎么实现的
源码:src/runtime/map.go
golang的map是内置关键字,不管是get还是set都需要通过key的hash找到对应的存储实体。具体的hash过程如下代码:
type maptype struct {
typ _type
key *_type
elem *_type
bucket *_type // internal type representing a hash bucket
keysize uint8 // size of key slot
elemsize uint8 // size of elem slot
bucketsize uint16 // size of bucket
flags uint32
}
//t *maptype
alg := t.key.alg
hash := alg.hash(key, uintptr(h.hash0))
这里其实就是拿到key对应的类型,然后获取当前key的类型的hash算法。然后调用hash函数。
hash函数的定义在这里:
// typeAlg is also copied/used in reflect/type.go.
// keep them in sync.
type typeAlg struct {
// function for hashing objects of this type
// (ptr to object, seed) -> hash
hash func(unsafe.Pointer, uintptr) uintptr
// function for comparing objects of this type
// (ptr to object A, ptr to object B) -> ==?
equal func(unsafe.Pointer, unsafe.Pointer) bool
}
可以知道,入参是指向key的指针,第二个参数是hash种子。
golang里面的对象的hash原理
golang里面每种数据类型的hash是与数据类型强相关的,并且是由编译器负责做类型绑定的。在src/runtime/alg.go
里面定义个一个map[type]typeAlg,用于表示每个数据类型对应的 typeAlg。
var algarray = [alg_max]typeAlg{
alg_NOEQ: {
nil, nil},
alg_MEM0: {
memhash0, memequal0},
alg_MEM8: {
memhash8, memequal8},
alg_MEM16: {
memhash16, memequal16},
alg_MEM32: {
memhash32, memequal32},
alg_MEM64: {
memhash64, memequal64},
alg_MEM128: {
memhash128, memequal128},
alg_STRING: {
strhash, strequal},
alg_INTER: {
interhash, interequal},
alg_NILINTER: {
nilinterhash, nilinterequal},
alg_FLOAT32: {
f32hash, f32equal},
alg_FLOAT64: {
f64hash, f64equal},
alg_CPLX64: {
c64hash, c64equal},
alg_CPLX128: {
c128hash, c128equal},
}
上面的hash函数,最后实际上底层都会调用:func memhash(p unsafe.Pointer, seed, s uintptr) uintptr
函数。以strhash
函数为例:
func strhash(a unsafe.Pointer, h uintptr) uintptr {
x := (*stringStruct)(a)
return memhash(x.str, h, uintptr(x.len))
}
下面看一下memhash
函数, src/runtime/hash64.go
// p表示需要hash的对象的地址
// seed 是hash 种子,
// s是需要hash的对象的字节数
func memhash(p unsafe.Pointer, seed, s uintptr) uintptr {
if (GOARCH == "amd64" || GOARCH == "arm64") &&
GOOS != "nacl" && useAeshash {
return aeshash(p, seed, s)
}
h := uint64(seed + s*hashkey[0])
tail:
switch {
case s == 0:
case s < 4:
h ^= uint64(*(*byte)(p))
h ^= uint64(*(*byte)(add(p, s>>1))) << 8
h ^= uint64(*(*byte)(add(p, s-1))) << 16
h = rotl_31(h*m1) * m2
case s <= 8:
h ^= uint64(readUnaligned32(p))
h ^= uint64(readUnaligned32(add(p, s-