Java实现布隆过滤器
一、概述
布隆过滤器本质上是一个很长的二进制数组,主要用来判断一个数据存不存在数组里,如果存在就用1表示,不存在用0表示,它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。
二、实现原理
当一个元素被加入集合时,通过 K 个散列函数将这个元素映射成一个位数组中的 K 个点(offset),把它们置为 1。检索时,我们只要看看这些点是不是都是 1 就(大约)知道集合中有没有它了,如果这些点有任何一个 0,则被检元素一定不在;如果都是 1,则被检元素很可能在。这就是布隆过滤器的基本思想。
布隆过滤器(Bloom Filter)是一个高空间利用率的概率性数据结构,由二进制向量(即位数组)和一系列随机映射函数(即哈希函数)两部分组成。
当布隆过滤器判定某个值存在时,其实这个值只是有可能存在;当它说某个值不存在时,那这个值肯定不存在,这个误判概率大约在 1% 左右。
1.布隆过滤器-添加元素
当使用布隆过滤器添加 key 时,会使用不同的 hash 函数对 key 存储的元素值进行哈希计算,从而会得到多个哈希值。根据哈希值计算出一个整数索引值,将该索引值与位数组长度做取余运算,最终得到一个位数组位置,并将该位置的值变为 1。每个 hash 函数都会计算出一个不同的位置,然后把数组中与之对应的位置变为 1。通过上述过程就完成了元素添加操作。
2.布隆过滤器-判定元素是否存在
当我们需要判断一个元素是否存时,首先对给定元素再次执行哈希计算,得到与添加元素时相同的位数组位置,判断所得位置是否都为 1,如果其中有一个为 0,那么说明元素不存在,若都为 1,则说明元素有可能存在。
三、布隆过滤器使用场景
1.解决Redis缓存穿透问题。
2.邮箱系统的垃圾邮件过滤功能也普遍用到了布隆过滤器,用了这个过滤器,平时也会遇到某些正常的邮件被放进了垃圾邮件目录中。
3.内容推荐,布隆过滤器能准确过滤掉那些已经看过的内容,没有看过的新内容,它也会过滤掉极小一部分 (误判),但是绝大多数新内容它都能准确识别。
四、布隆过滤器实现方式
1.引入Guava的依赖实现
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.0.1-jre</version>
</dependency>
2.代码实现如下:
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
public class BloomFilterTest {
public static void main(String[] args) {
// 预期插入数量
long capacity = 100000L;
// 错误比率
double errorRate = 0.001;
//创建BloomFilter对象,需要传入Funnel对象,预估的元素个数,错误率
BloomFilter<Long> filter = BloomFilter.create(Funnels.longFunnel(), capacity, errorRate);
//put值进去
for (long i = 0; i < capacity; i++) {
filter.put(i);
}
// 统计误判次数
int count = 0;
// 我在数据范围之外的数据,测试相同量的数据,判断错误率是不是符合我们当时设定的错误率
for (long i = capacity; i < capacity * 2; i++) {
if (filter.mightContain(i)) {
count++;
}
}
System.out.println(count);
}
}
结果为:假如数据为100000容错率为0.001,统计出来的误判个数是94。
因此,布隆过滤器容错还是非常可以的,当然也可以通过redis实现布隆过滤器,这里就不说明了。