当前位置: 首页 > 科技观察

阿里高频面试题:如何快速判断一个元素是否在集合中?

时间:2023-03-19 11:27:46 科技观察

如何快速判断一个元素是否在集合中?这个问题是我最近在面试的时候经常问的一个问题。对于这个问题,不同的人有不同的答案。今天要介绍一个很少有人会提到的解决方案,那就是借助Bloomfilter。什么是布隆过滤器布隆过滤器(BloomFilter)是1970年由一个叫Bloom的师兄提出来的。实际上,它可以看作是一个由两部分组成的数据结构:一个二进制向量(或位数组)和一系列随机映射函数(哈希函数)。它的优点是空间效率和查询时间都比一般算法好很多,缺点是存在一定的误识别率和删除难度。实现原理首先,让我们来了解一下布隆过滤器算法。Bloomfilter算法的主要思想是使用n个哈希函数进行哈希运算得到不同的哈希值,根据哈希值将它们映射到一个数组中(这个数组的长度可能会很长)。不同的索引位置,然后将相应索引位上的值设置为1。判断元素是否出现在集合中是使用k个不同的哈希函数计算哈希值,并检查该值是否对应于对应的索引位置hash值为1,如果其中一个不为1,说明该元素不存在于集合中。但是也可以判断元素在集合中,但是元素不在。1上面这个元素的所有索引位置都是由其他元素设置的,这就导致了一定概率的误判(这就是为什么上面在一个集合中是活的根本原因,因为会存在一些hash冲突)。注:误报率越低,对应的性能越低。布隆过滤器可以用来判断一个元素是否(可能)在一个集合中,与其他数据结构相比,布隆过滤器在空间和时间上具有巨大的优势。请注意上面的一个词:可能。这里留个悬念,下面详细分析。使用场景判断给定数据是否存在,防止缓存穿透(判断请求的数据是否有效,避免直接绕过缓存请求数据库)等,垃圾邮件过滤,黑名单功能等。看完算法思路布隆过滤器,下面开始讲解具体实现。先给大家举个例子,假设有旺财和小强两个字符串,分别经过三次hash算法,然后设置对应数组索引位置的值(假设数组长度为16)为1根据哈希结果,我们先来看词组旺财:旺财经过三次哈希后,值分别为2、4、6,那么可以得到索引值分别为2、4、6,所以数组的索引(2,4,6)位置的值设置为1,其余的都视为0。现在假设你需要搜索旺财。同样的三个哈希后,你发现得到的索引2、4、6对应的位置的值都是1,那么你就可以判断Prosperity可能存在。然后将小强插入布隆过滤器。实际过程同上,假设得到的下标为1、3、5。抛开旺财的存在,此时小强是这样在Bloomfilter中的,实际的数组结合旺财和小强看起来是这样的:现在有一个数据:9527,现在的需求是判断9527是否存在,假设经过3次hash得到的9527的下标分别是:5、6、7。原来下标为7的位置的值为0,所以可以肯定的判断9527一定不存在。接着来了一个国产的007,经过3次hash,得到的下标分别是:2、3、5。结果发现下标2、3、5对应的值都是1,所以可以大致判断出国产007可能存在。.但实际上经过我们刚才的演示,国产的007根本就不存在。之所以2、3、5索引位置的值为1是因为其他的数据设置。说了这么多,不知道大家是否理解布隆过滤器的作用。代码实现作为Java程序员,我们真的很高兴。我们用了很多框架和工具,基本上都是封装好的。对于布隆过滤器,我们使用谷歌封装的工具类。首先添加依赖com.google.guavaguava25.1-jre/dependency>代码implementationimportcom.google.common.hash.BloomFilter;importcom.google.common.hash.Funnels;importjava.nio.charset.Charset;publicclassBloomFilterDemo{publicstaticvoidmain(String[]args){/***创建插入对象为1亿,误报率为0.01%的Bloomfilter*不存在,一定不存在*存在,不一定存在*--------------*Funnelobject:estimatedelementNumber,误判率*mightContain:判断元素是否存在的方法*/BloomFilterbloomFilter=BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")),100000000,0.0001);bloomFilter.put("dead");bloomFilter.put("敲门声");bloomFilter.put("Redis");System.out.println(bloomFilter.mightContain("Redis"));System.out.println(bloomFilter.mightContain("Java"));}}具体解释已经en写在评论里。至此,相信大家一定对Bloomfilter以及如何使用有了一定的了解。实战中,我们来模拟这样一个场景:通过布隆过滤器解决缓存穿透。首先你知道什么是缓存穿透吗?缓存穿透是指用户访问缓存或数据库中不存在的数据。因为不存在缓存中,所以并发高的时候会去访问数据库。很容易让数据库不堪重负,那么布隆过滤器是如何解决这个问题的呢?他的原理是这样的:把数据库中所有的查询条件都放到布隆过滤器中。当查询请求到来时,首先会通过布隆过滤器进行检查。如果判断请求的查询值存在,则继续检查;如果判断请求query不存在,则直接丢弃。代码如下:Stringget(Stringkey){Stringvalue=redis.get(key);if(value==null){if(!bloomfilter.mightContain(key)){returnnull;}else{value=db.get(key);redis.set(key,value);}}returnvalue;}总结本文详细介绍什么是布隆过滤器?效果如何?从代码层面从各个方面对实现原理和Bloomfilter进行了讲解。学习可以帮助您迈向高级学习。