布隆过滤器四两拨千斤的作用真的牛!绝对干货分享,java面试问什么问题

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

主要看论文的这个地方:

它的工作原理,总结起来是这样的:

它有两个 hash 表,记为 T1,T2。

两个 hash 函数,记为 h1,h2。

当一个不存在的元素插入的时候,会先根据 h1 计算出其在 T1 表的位置,如果该位置为空则可以放进去。

如果该位置不为空,则根据 h2 计算出其在 T2 表的位置,如果该位置为空则可以放进去。

如果该位置不为空,就把当前位置上的元素踢出去,然后把当前元素放进去就行了。

也可以随机踢出两个位置中的一个,总之会有一个元素被踢出去。

被踢出去的元素怎么办呢?

没事啊,它也有自己的另外一个位置。

论文中的伪代码是这样的:

看不懂没关系,我们画个示意图:

上面的图说的是这样的一个事儿:

我想要插入元素 x,经过两个 hash 函数计算后,它的两个位置分别为 T1 表的 2 号位置和 T2 表的 1 号位置。

两个位置都被占了,那就随机把 T1 表 2 号位置上的 y 踢出去吧。

而 y 的另一个位置被 z 元素占领了。

于是 y 毫不留情把 z 也踢了出去。

z 发现自己的备用位置还空着(虽然这个备用位置也是元素 v 的备用位置),赶紧就位。

所以,当 x 插入之后,图就变成了这样:

上面这个图其实来源就是论文里面:

这种类似于套娃的解决方式看似可行,但是总是有出现循环踢出导致放不进 x 的问题。

比如上图中的(b)。

当遇到这种情况时候,说明布谷鸟 hash 已经到了极限情况,应该进行扩容,或者 hash 函数的优化。

所以,你再次去看伪代码的时候,你会明白里面的 MaxLoop 的含义是什么了。

这个 MaxLoop 的含义就是为了避免相互踢出的这个过程执行次数太多,设置的一个阈值。

其实我理解,布谷鸟 hash 是一种解决 hash 冲突的骚操作。

如果你想上手玩一下,可以访问这个网站:

http://www.lkozma.net/cuckoo_hashing_visualization/

当踢来踢去了 16 (MaxLoop)次还没插入完成后,它会告诉你,需要 rehash 并对数组扩容了:

布谷鸟 hash 就是这么一回事。

接着,我们看布谷鸟过滤器。

布谷鸟过滤器

布谷鸟过滤器的论文《Cuckoo Filter: Practically Better Than Bloom》开篇第一页,里面有这样一段话。

直接和布隆过滤器正面刚:我布谷鸟过滤器,就是比你屌一点。

上来就指着别人的软肋怼:

标准的布隆过滤器的一大限制是不能删除已经存在的数据。如果使用它的变种,比如 Counting Bloom Filter,但是空间却被撑大了 3 到 4 倍,巴拉巴拉巴拉…

而我就不一样了:

这篇论文将要证明的是,与标准布隆过滤器相比,支持删除并不需要在空间或性能上提出更高的开销。

布谷鸟过滤器是一个实用的数据结构,提供了四大优势:

1.支持动态的新增和删除元素。2.提供了比传统布隆过滤器更高的查找性能,即使在接近满的情况下(比如空间利用率达到 95% 的时候)。3.比诸如商过滤器(quotient filter,另一种过滤器)之类的替代方案更容易实现。4.如果要求错误率小于3%,那么在许多实际应用中,它比布隆过滤器占用的空间更小。

布谷鸟过滤器的 API 无非就是插入、查询和删除嘛。

其中最重要的就是插入,看一下:

论文中的部分,你大概瞟一眼,看不明白没关系,我这不是马上给你分析一波吗。

插入部分的伪代码,可以看到一点布谷鸟 hash 的影子,因为就是基于这个东西来的。

那么最大的变化在什么地方呢?

无非就是 hash 函数的变化。

看得我目瞪狗呆,心想:还有这种骚操作呢?

首先,我们回忆一下布谷鸟 hash,它存储的是插入元素的原始值,比如 x,x 会经过两个 hash 函数,如果我们记数组的长度为 L,那么就是这样的:

p1 = hash1(x) % Lp2 = hash2(x) % L

而布谷鸟过滤器计算位置是怎样的呢?

h1(x) = hash(x),h2(x) = h1(x) ⊕ hash(x’s fingerprint).

我们可以看到,计算 h2(位置2)时,对 x 的 fingerprint 进行了一个 hash 计算。

“指纹”的概念一会再说,我们先关注位置的计算。

上面算法中的异或运算确保了一个重要的性质:位置 h2 可以通过位置 h1 和 h1 中存储的“指纹”计算出来。

说人话就是:只要我们知道一个元素的位置(h1)和该位置里面存储的“指纹”信息,那么我们就可以知道该“指纹”的备用位置(h2)。

因为使用的异或运算,所以这两个位置具有对偶性。

只要保证 hash(x’s fingerprint) !=0,那么就可以确保 h2!=h1,也就可以确保,不会出现自己踢自己的死循环问题。

另外,为什么要对“指纹”进行一个 hash 计算之后,在进行异或运算呢?

论文中给出了一个反证法:如果不进行 hash 计算,假设“指纹”的长度是 8bit,那么其对偶位置算出来,距离当前位置最远也才 256。

为啥,论文里面写了:

因为如果“指纹”的长度是 8bit,那么异或操作只会改变当前位置 h1(x) 的低 8 位,高位不会改变。

就算把低 8 位全部改了,算出来的位置也就是我刚刚说的:最远 256 位。

所以,对“指纹”进行哈希处理可确保被踢出去的元素,可以重新定位到哈希表中完全不同的存储桶中,从而减少哈希冲突并提高表利用率。

然后这个 hash 函数还有个问题你发现了没?

它没有对数组的长度进行取模,那么它怎么保证计算出来的下标一定是落在数组中的呢?

这个就得说到布谷鸟过滤器的另外一个限制了。

其强制数组的长度必须是 2 的指数倍。

2 的指数倍的二进制一定是这样的:10000000…(n个0)。

这个限制带来的好处就是,进行异或运算时,可以保证计算出来的下标一定是落在数组中的。

这个限制带来的坏处就是:

布谷鸟过滤器:我支持删除操作。布隆过滤器:我不需要限制长度为 2 的指数倍。布谷鸟过滤器:我查找性能比你高。布隆过滤器:我不需要限制长度为 2 的指数倍。布谷鸟过滤器:我空间利用率也高。布隆过滤器:我不需要限制长度为 2 的指数倍。布谷鸟过滤器:我烦死了,TMD!

接下来,说一下“指纹”。

这是论文中第一次出现“指纹”的地方。

“指纹”其实就是插入的元素进行一个 hash 计算,而 hash 计算的产物就是 几个 bit 位。

布谷鸟过滤器里面存储的就是元素的“指纹”。

查询数据的时候,就是看看对应的位置上有没有对应的“指纹”信息:

删除数据的时候,也只是抹掉该位置上的“指纹”而已:

由于是对元素进行 hash 计算,那么必然会出现“指纹”相同的情况,也就是会出现误判的情况。

没有存储原数据,所以牺牲了数据的准确性,但是只保存了几个 bit,因此提升了空间效率。

说到空间利用率,你想想布谷鸟 hash 的空间利用率是多少?

在完美的情况下,也就是没有发生哈希冲突之前,它的空间利用率最高只有 50%。

因为没有发生冲突,说明至少有一半的位置是空着的。

除了只存储“指纹”,布谷鸟过滤器还能怎么提高它的空间利用率的呢?

看看论文里面怎么说的:

前面的(a)、(b)很简单,还是两个 hash 函数,但是没有用两个数组来存数据,就是基于一维数组的布谷鸟 hash ,核心还是踢来踢去,不多说了。

重点在于©,对数组进行了展开,从一维变成了二维。

每一个下标,可以放 4 个元素了。

这样一个小小的转变,空间利用率从 50% 直接到了 98%:

我就问你怕不怕?

上面截图的论文中的第一点就是在陈述这样一个事实:

当 hash 函数固定为 2 个的时候,如果一个下标只能放一个元素,那么空间利用率是 50%。

但是如果一个下标可以放 2,4,8 个元素的时候,空间利用率就会飙升到 84%,95%,98%。

到这里,我们明白了布谷鸟过滤器对布谷鸟 hash 的优化点和对应的工作原理。

看起来一切都是这么的完美。

各项指标都比布隆过滤器好,主打的是支持删除的操作。

但是真的这么好吗?

当我看到论文第六节的这一段的时候,沉默了:

对重复数据进行限制:如果需要布谷鸟过滤器支持删除,它必须知道一个数据插入过多少次。不能让同一个数据插入 kb+1 次。其中 k 是 hash 函数的个数,b 是一个下标的位置能放几个元素。

比如 2 个 hash 函数,一个二维数组,它的每个下标最多可以插入 4 个元素。那么对于同一个元素,最多支持插入 8 次。

例如下面这种情况:

why 已经插入了 8 次了,如果再次插入一个 why,则会出现循环踢出的问题,直到最大循环次数,然后返回一个 false。

怎么避免这个问题呢?

我们维护一个记录表,记录每个元素插入的次数就行了。

虽然逻辑简单,但是架不住数据量大呀。你想想,这个表的存储空间又怎么算呢?

想想就难受。

如果你要用布谷鸟过滤器的删除操作,那么这份难受,你不得不承受。

最后,再看一下各个类型的过滤器的对比图吧:

最后

每年转战互联网行业的人很多,说白了也是冲着高薪去的,不管你是即将步入这个行业还是想转行,学习是必不可少的。作为一个Java开发,学习成了日常生活的一部分,不学习你就会被这个行业淘汰,这也是这个行业残酷的现实。

如果你对Java感兴趣,想要转行改变自己,那就要趁着机遇行动起来。或许,这份限量版的Java零基础宝典能够对你有所帮助。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
.(img-Nc3lDIUd-1713467626025)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-EhCjLuKO-1713467626026)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值