MurmurHash介绍
MurmurHash是一种非加密型哈希函数,和其它流行哈希函数相比,对于规律性较强的key随机分布特性表现更良好,在很多开源的软件项目(Redis,Memcached,Cassandra,HBase,Lucene都用它)都有使用。有以下几个特性:
- 随机分布特性表现好
- 算法速度快(都是位移操作)
go实现java版的MurmurHash
项目有一段逻辑用到了MurmurHash2.0,项目语言是java,现在需要用go来实现一遍,网上有很多go语言的实现,不过都有一个问题,就是java版返回的是long类型,而go语言返回的是uint64,导致两个版本对同一个key进行hash但是返回的结果不同,而我们需要go语言的实现和java版本返回的结果一致才行,所以需要要基于开源go实现版本进行改造才行。
java实现:
public static long hash64A(ByteBuffer buf, int seed) {
ByteOrder byteOrder = buf.order();
buf.order(ByteOrder.LITTLE_ENDIAN);
long m = 0xc6a4a7935bd1e995L;
int r = 47;
long h = seed ^ (buf.remaining() * m);
long k;
while (buf.remaining() >= 8) {
k = buf.getLong();
k *= m;
k ^= k >>> r;
k *= m;
h ^= k;
h *= m;
}
if (buf.remaining() > 0) {
ByteBuffer finish = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
// for big-endian version, do this first:
// finish.position(8-buf.remaining());
finish.put(buf).rewind();
h ^= finish.getLong();
h *= m;
}
h ^= h >>> r;
h *= m;
h ^= h >>> r;
buf.order(byteOrder);
return h;
}
java返回结果是long类型,go语言实现的难点是要把go的uint64转成对应的int64,以及位移操作时需要把int64转换成uint64,经过了不断的调试,最终成功用go实现了一版和java逻辑一样的代码,经过测试10万个key,java版和go版本hash结果都一致,符合要求。
go实现:
package hash
const (
BIG_M = 0xc6a4a7935bd1e995
BIG_R = 47
SEED = 0x1234ABCD
)
func MurmurHash64A(data []byte) (h int64) {
var k int64
h = SEED ^ int64(uint64(len(data))*BIG_M)
var ubigm uint64 = BIG_M
var ibigm = int64(ubigm)
for l := len(data); l >= 8; l -= 8 {
k = int64(int64(data[0]) | int64(data[1])<<8 | int64(data[2])<<16 | int64(data[3])<<24 |
int64(data[4])<<32 | int64(data[5])<<40 | int64(data[6])<<48 | int64(data[7])<<56)
k := k* ibigm
k ^= int64(uint64(k) >> BIG_R)
k = k* ibigm
h = h^k
h = h* ibigm
data = data[8:]
}
switch len(data) {
case 7:
h ^= int64(data[6]) << 48
fallthrough
case 6:
h ^= int64(data[5]) << 40
fallthrough
case 5:
h ^= int64(data[4]) << 32
fallthrough
case 4:
h ^= int64(data[3]) << 24
fallthrough
case 3:
h ^= int64(data[2]) << 16
fallthrough
case 2:
h ^= int64(data[1]) << 8
fallthrough
case 1:
h ^= int64(data[0])
h *= ibigm
}
h ^= int64(uint64(h) >> BIG_R)
h *= ibigm
h ^= int64(uint64(h) >> BIG_R)
return
}