来自 Scala 和 Guava 的 Murmur3 的不同结果
Different results from Murmur3 from Scala and Guava
我正在尝试使用 Murmur3 算法生成哈希值。哈希是一致的,但它们是 Scala 和 Guava 返回的不同值。
class package$Test extends FunSuite {
test("Generate hashes") {
println(s"Seed = ${MurmurHash3.stringSeed}")
val vs = Set("abc", "test", "bucket", 111.toString)
vs.foreach { x =>
println(s"[SCALA] Hash for $x = ${MurmurHash3.stringHash(x).abs % 1000}")
println(s"[GUAVA] Hash for $x = ${Hashing.murmur3_32().hashString(x).asInt().abs % 1000}")
println(s"[GUAVA with seed] Hash for $x = ${Hashing.murmur3_32(MurmurHash3.stringSeed).hashString(x).asInt().abs % 1000}")
println()
}
}
}
Seed = -137723950
[SCALA] Hash for abc = 174
[GUAVA] Hash for abc = 419
[GUAVA with seed] Hash for abc = 195
[SCALA] Hash for test = 588
[GUAVA] Hash for test = 292
[GUAVA with seed] Hash for test = 714
[SCALA] Hash for bucket = 413
[GUAVA] Hash for bucket = 22
[GUAVA with seed] Hash for bucket = 414
[SCALA] Hash for 111 = 250
[GUAVA] Hash for 111 = 317
[GUAVA with seed] Hash for 111 = 958
为什么我得到不同的哈希值?
在我看来,Scala 的 hashString
将 UTF-16 char
对转换为 int
的方式不同于 Guava 的 hashUnencodedChars
(hashString
与没有 Charset
重命名为那个)。
斯卡拉:
val data = (str.charAt(i) << 16) + str.charAt(i + 1)
番石榴:
int k1 = input.charAt(i - 1) | (input.charAt(i) << 16);
在 Guava 中,索引 i
处的 char
变为 int
处的 16 个最低有效位,而 i + 1
处的 char
变为最重要的 16 位。在 Scala 实现中,情况正好相反:i
处的 char
是 最重要的 而 i + 1
处的 char
是 最不重要。 (我想 Scala 实现使用 +
而不是 |
的事实也可能很重要。)
请注意,Guava 实现相当于使用 ByteBuffer.putChar(c)
两次将两个字符放入小端 ByteBuffer
,然后使用 ByteBuffer.getInt()
取回一个 int 值。 Guava 实现 也 相当于使用 UTF-16LE
将字符编码为字节并散列这些字节。 Scala 实现不等同于使用 JVM 需要支持的任何标准字符集对字符串进行编码。总的来说,我不确定 Scala 有什么先例(如果有的话)按照它的方式来做。
编辑:
Scala 实现还做了另一件不同于 Guava 实现的事情:它将要散列的 chars 的数量传递给 finalizeHash
方法,Guava 的实现通过字节数到等效的fmix
方法。
我相信 hashString(x, StandardCharsets.UTF_16BE)
应该符合 Scala 的行为。让我们知道。
(另外,请将您的 Guava 升级到更新的版本!)
我正在尝试使用 Murmur3 算法生成哈希值。哈希是一致的,但它们是 Scala 和 Guava 返回的不同值。
class package$Test extends FunSuite {
test("Generate hashes") {
println(s"Seed = ${MurmurHash3.stringSeed}")
val vs = Set("abc", "test", "bucket", 111.toString)
vs.foreach { x =>
println(s"[SCALA] Hash for $x = ${MurmurHash3.stringHash(x).abs % 1000}")
println(s"[GUAVA] Hash for $x = ${Hashing.murmur3_32().hashString(x).asInt().abs % 1000}")
println(s"[GUAVA with seed] Hash for $x = ${Hashing.murmur3_32(MurmurHash3.stringSeed).hashString(x).asInt().abs % 1000}")
println()
}
}
}
Seed = -137723950
[SCALA] Hash for abc = 174
[GUAVA] Hash for abc = 419
[GUAVA with seed] Hash for abc = 195
[SCALA] Hash for test = 588
[GUAVA] Hash for test = 292
[GUAVA with seed] Hash for test = 714
[SCALA] Hash for bucket = 413
[GUAVA] Hash for bucket = 22
[GUAVA with seed] Hash for bucket = 414
[SCALA] Hash for 111 = 250
[GUAVA] Hash for 111 = 317
[GUAVA with seed] Hash for 111 = 958
为什么我得到不同的哈希值?
在我看来,Scala 的 hashString
将 UTF-16 char
对转换为 int
的方式不同于 Guava 的 hashUnencodedChars
(hashString
与没有 Charset
重命名为那个)。
斯卡拉:
val data = (str.charAt(i) << 16) + str.charAt(i + 1)
番石榴:
int k1 = input.charAt(i - 1) | (input.charAt(i) << 16);
在 Guava 中,索引 i
处的 char
变为 int
处的 16 个最低有效位,而 i + 1
处的 char
变为最重要的 16 位。在 Scala 实现中,情况正好相反:i
处的 char
是 最重要的 而 i + 1
处的 char
是 最不重要。 (我想 Scala 实现使用 +
而不是 |
的事实也可能很重要。)
请注意,Guava 实现相当于使用 ByteBuffer.putChar(c)
两次将两个字符放入小端 ByteBuffer
,然后使用 ByteBuffer.getInt()
取回一个 int 值。 Guava 实现 也 相当于使用 UTF-16LE
将字符编码为字节并散列这些字节。 Scala 实现不等同于使用 JVM 需要支持的任何标准字符集对字符串进行编码。总的来说,我不确定 Scala 有什么先例(如果有的话)按照它的方式来做。
编辑:
Scala 实现还做了另一件不同于 Guava 实现的事情:它将要散列的 chars 的数量传递给 finalizeHash
方法,Guava 的实现通过字节数到等效的fmix
方法。
我相信 hashString(x, StandardCharsets.UTF_16BE)
应该符合 Scala 的行为。让我们知道。
(另外,请将您的 Guava 升级到更新的版本!)