哈希:比Object.hashCode()
提供的更复杂的哈希工具,包括布隆过滤器。
1.概述
Java固有的哈希码概念被限制为32位,并且在哈希算法和它们所作用的数据之间没有分隔,因此替代的哈希算法不容易被替换。同样,hashCode的实现往往质量较差,部分原因是它们最终依赖于其他现有的质量较差的hashCode实现,包括许多JDK类中的实现。
Object.hashCode实现通常非常快,但是具有较弱的冲突预防功能,并且不期望出现位分散。这使它们非常适合在哈希表中使用,因为额外的冲突只会对性能造成轻微的影响,而不良的位分散则可以使用辅助哈希函数(使用Java中所有合理的哈希表实现)轻松纠正。然而,对于哈希函数在简单哈希表之外的许多用途,Object.hashCode
几乎总是不足——因此有了com.google.common.hash
。
2.组成
在查看Javadoc包时,我们看到了许多不同的类型,但是它们如何组合在一起并不明显。
让我们来看一段使用该库的示例代码。
HashFunction hf = Hashing.md5();
HashCode hc = hf.newHasher()
.putLong(id)
.putString(name, Charsets.UTF_8)
.putObject(person, personFunnel)
.hash();
2.1HashFunction
HashFunction
是一个纯无状态函数,它将任意数据块映射到固定数量的位,其属性是相等的输入始终产生相等的输出,而不相等的输入则尽可能每次产生不相等的输出。
2.2Hasher
可以向HashFunction
请求有状态的Hasher
,它提供了流利的语法以将数据添加到哈希中,然后检索哈希值。Hasher
可以接受任何原生输入、字节数组、字节数组的片段、字符序列、某些字符集中的字符序列等,或任何其他带有合适的Funnel
的Object
。
Hasher
实现了PrimitiveSink
接口,该接口为接受原生值流的对象指定了流利的API。
2.3Funnel
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)
,因为它们产生了相同的字节序列。这可能会导致意外的哈希冲突。添加某种分隔符可以帮助消除意外的哈希冲突。
2.4HashCode
一旦为Hasher
提供了所有输入,就可以使用其hash()
方法检索HashCode
。HashCode
支持相等性测试等,以及asInt()
、asLong()
、asBytes()
方法,还支持writeBytesTo(array, offset, maxLength)
,它将哈希的第一个maxLength
字节写入数组。
3.BloomFilter
布隆过滤器是一个很好的哈希应用程序,不能简单地使用Object.hashCode()
来完成。简而言之,布隆过滤器是一种概率数据结构,可让你测试某个对象是否绝对不在过滤器中,或者可能已添加到Bloom过滤器中。维基百科页面非常全面,我们推荐使用本教程。
我们的哈希库具有内置的布隆过滤器实现,只需要你实现一个Funnel
即可将你的类型分解为原生类型。你可以使用create(Funnel funnel, int expectedInsertions, double falsePositiveProbability)
获得新的BloomFilter
,或者只接受默认的3%错误概率。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(dude)) {
// the probability that dude reached this place if he isn't a friend is 1%
// we might, for example, start asynchronously loading things for dude while we do a more expensive exact check
}
4.Hashing
Hashing
工具类提供了许多用于对HashCode
对象进行操作的签名哈希函数和工具。
4.1提供的哈希函数
4.2HashCode操作
方法 | 描述 |
---|---|
HashCodecombineOrdered(Iterable) | 以有序方式组合哈希码,因此,如果从此方法获得的两个哈希相同,则很可能每个哈希都是以相同的顺序从相同的哈希中计算得出的。 |
HashCodecombineUnordered(Iterable) | 以无序方式组合哈希码,因此,如果从此方法获得的两个哈希相同,则很可能每个哈希都是以相同的顺序从相同的哈希中计算得出的。 |
intconsistentHash(HashCode,intbuckets) | 为哈希码分配一个一致的“桶”,这样可以最大限度地减少随着bucket桶数量的增加而重新映射的需要。有关详细信息,请参见维基百科。 |
本文参考:
HashingExplained
guava-tests-hash
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。