哈希(Hashing)
概览
Java的内嵌的hash代码的概念被限制到32位,而且没有提供hash算法和他们处理的数据之间提供分离,所以替代的hash算法不能非常容易的被替换。而且hashCode的实现往往质量很差,在某种程度上因为他们最终依赖其他已存在的低质量hashCode实现,包含在许多JDK中的实现。
Object.hashCode
实现往往非常快,但是存在非常弱的防碰撞功能和没有位分离的期望。这使得他们非常适合在哈希表中使用,因为额外的碰撞只会对性能造成轻微的影响,而糟糕的位分散很容易使用二级哈希函数(Java中所有的合理的哈希表实现都使用)来纠正。对于在简单哈希表之外的许多哈希函数的用途,Object.hashCode
总是达不到要求 – 因此出现了com.google.common.hash
。
结构
查看包的Javadoc,我们看到很多不同的类型,但是他们如何组合到一起并不明显。
我们看一段使用此类库简单的代码。
HashFunction hf = Hashing.md5();
HashCode hc = hf.newHasher()
.putLong(id)
.putString(name, Charsets.UTF_8)
.putObject(person, personFunnel)
.hash();
HashFunction
HashFunction
是纯粹的无状态函数,它将一个任意的数据块映射到固定数量的bit,具有相等的输入总是产出相等的输出,不相等的输入总是不相等的输出的属性。
Hasher
可以向HashFunction
请求有状态的Hasher
,其提供流式语法来添加数据到hash,然后检索hash值。Hasher
可以接受任何原生类型输入,字节数组,字节数组片,字符串,某些字符集字符串等等,或者通过合适的Funnel
提供的其他Object
。
Hasher
实现了PrimitiveSink
接口,此接口为接受原生类型值的对象指定流式API。
Funnel
Funnel
描述了如何将特别的对象分解为原生类型字段值。例如,如果我们有:
class Person {
final int id;
final String firstName;
final String lastName;
final int birthYear;
}
我们的Funnel
可能看起来像:
Funnel<Person> personFunnel = new Funnel<Person>() {
@Override
public void funnel(Person person, PrimitiveSink into) {
into
.putInt(person.id)
.putString(person.firstName, Charsets.UTF_8)
.putString(person.lastName, Charsets.UTF_8)
.putInt(birthYear);
}
};
注意:putString("abc", Charsets.UTF_8).putString("def", Charsets.UTF_8)
是完全等价于putString("ab", Charsets.UTF_8).putString("cdef", Charsets.UTF_8)
,因为他们产生相同的字节序列。这可能会导致意外的哈希冲突。添加某种分割器可以帮助消除意外的哈希冲突。
HashCode
一旦Hasher
已经给出所有它的输入,它的hash()
方法就可以用来检索一个HashCode
。HashCode
支持相等性测试等,以及asInt()
,asLong()
,asBytes()
方法以及额外的writeBytesTo(array, offset, maxLength)
,其将hash的第一个maxLength
字节写入到数组。
BloomFilter(布隆过滤器)
Bloom过滤器是一个哈希的令人愉快的应用程序,不能简单地使用Object.hashCode()
来完成。简单来说,Bloom过滤器是概率性的数据结构,允许你测试一个对象是否明确在过滤器中,或者可能被添加到Bloom过滤器中。 Wikipedia还算全面,我们建议这个教程。
我们的哈希类库有一个内建的Bloom过滤器实现,只要求你实现一个Funnel
将你的类型分解为原生类型。你可以使用create(Funnel funnel, int expectedInsertions, double falsePositiveProbability)
获取新颖的BloomFilter<T>
,或者只接收默认的3%的false可能性。BloomFilter<T>
提供boolean mightContain(T)
和void put(T)
方法,这两个方法不言自明的。
BloomFilter<Person> friends = BloomFilter.create(personFunnel, 500, 0.01);
for (Person friend : friendsList) {
friends.put(friend);
}
// much later
if (friends.mightContain(someone)) {
// the probability that someone reached this place if they aren't a friend is
// 1% we might, for example, start asynchronously loading things for someone
// while we do a more expensive exact check
}
哈希
Hashing
工具类提供多个常用的哈希函数和工具来操作HashCode
对象。
提供的哈希函数
要了解全部列表,请查看Hashing
文档
HashCode操作
方法 | 描述 |
---|---|
HashCode combineOrdered(Iterable<HashCode>) | 以有序的方式组合哈希码,如果从此方法获取的两个哈希是相同的,则很可能每个哈希值都是以相同的顺序从相同的哈希值计算出来的 |
HashCode combineUnordered(Iterable<HashCode>) | 以无序的方式组合哈希码,如果从此方法获取的两个哈希值是相同的,则很可能每个哈希值都是以相同的顺序从相同的哈希值计算出来的 |
int consistentHash(HashCode, int buckets) | 为哈希码分配一致的“bucket(桶)”,以最大限度的减少随着桶的数量的增长而重新映射的需要。详情请查看Wikipedia |