前面我们已经讲过布隆过滤器的原理【实战问题】-- 缓存穿透之布隆过滤器(1),都理解是这么运行的,那么一般我们使用布隆过滤器,是怎么去使用呢?如果自己去实现,又是怎么实现呢?
布隆过滤器
再念一次定义:
布隆过滤器(Bloom Filter
)是由布隆(Burton Howard Bloom
)在 1970 年提出的,它实际上是由一个很长的二进制向量和一系列随机hash
映射函数组成(说白了,就是用二进制数组存储数据的特征)。
譬如下面例子:有三个hash
函数,那么“陈六”就会被三个hash
函数分别hash
,并且对位数组的长度,进行取余,分别hash到三个位置。
如果对原理还有不理解的地方,可以查看我的上一篇文章。
手写布隆过滤器
那么我们手写布隆过滤器的时候,首先需要一个位数组,在Java
里面有一个封装好的位数组,BitSet
。
简单介绍一下BitSet
,也就是位图,里面实现了使用紧凑的存储空间来表示大空间的位数据。使用的时候,我们可以直接指定大小,也就是相当于创建出指定大小的位数组。
BitSet bits = new BitSet(size);
同时,BitSet
提供了大量的API
,基本的操作主要包括:
- 清空位数组的数据
- 翻转某一位的数据
- 设置某一位的数据
- 获取某一位的数据
- 获取当前的
bitSet
的位数
下面就讲一下,写一个简单的布隆过滤器需要考虑的点:
- 位数组的大小空间,需要指定,其他相同的时候,位数组的大小越大,
hash
冲突的可能性越小。 - 多个
hash
函数,我们需要使用hash
数组来存,hash
函数需要如何设置呢?为了避免冲突,我们应该使用多个不同的质数来当种子。 - 方法:主要实现两个方法,一个往布隆过滤器里面添加元素,另一个是判断布隆过滤器是否包含某个元素。
下面是具体的实现,只是简单的模拟,不可用于生产环境,hash
函数较为简单,主要是使用hash
值得高低位进行异或,然后乘以种子,再对位数组大小进行取余数:
import java.util.BitSet;
public class MyBloomFilter {
// 默认大小
private static final int DEFAULT_SIZE = Integer.MAX_VALUE;
// 最小的大小
private static final int MIN_SIZE = 1000;
// 大小为默认大小
private int SIZE = DEFAULT_SIZE;
// hash函数的种子因子
private static final int[] HASH_SEEDS = new int[]{
3, 5, 7, 11, 13, 17, 19, 23, 29, 31};
// 位数组,0/1,表示特征
private BitSet bitSet = null;
// hash函数
private HashFunction[] hashFunctions = new HashFunction[HASH_SEEDS.length];
// 无参数初始化
public MyBloomFilter() {
// 按照默认大小
init();
}
// 带参数初始化
public MyBloomFilter(int size) {
// 大小初始化小于最小的大小
if (size >= MIN_SIZE) {
SIZE = size;
}
init();
}
private void init(