2023-08-17  阅读(37)
原文作者:LifeIsForSharing 原文地址:https://solang.blog.csdn.net/article/details/105101316

哈希:比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可以接受任何原生输入、字节数组、字节数组的片段、字符序列、某些字符集中的字符序列等,或任何其他带有合适的FunnelObject

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()方法检索HashCodeHashCode支持相等性测试等,以及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] ,回复【面试题】 即可免费领取。

阅读全文