1.引入
假设有一个数据集合,现在需要查找某个数据是否在这个集合中,那么就有一下几个方案:
1)将数据保存到数据库中
2)使用HashSet将数据保存起来,使用contains方法就可以判断该元素是否在集合中
3)使用Bit-Map,建立一个BitSet,将每个数据保存在其中
在数据量较小的时候,以上方法都可以解决问题,但是当数据量较大的时候,就会有问题:
1)数据量变得很大之后,关系型数据库查询的效率会很低,而且每来一个数据就要启动一次数据库查询太小题大做
2)太过于消耗内存,随着数据增多,占用的内存会越来越多,就算只有1亿的数据,每个数据8字节(long),就有700多兆。
3)消耗内存相对少,但是由于哈希函数用的就是数值,则可能会有很多空间的浪费
2.Bloom-Filter
1)原理
1>首先定义一个长度为n的比特数组,初始化全为0
0000 0000 0000 0000 000
上面的比特数组的n为20
2>选取k个哈希函数,这k个哈希函数产生的结果的值的范围为[0,n-1](对于上面的比特数组,即0到19)。对每个要添加进集合的数据进行哈希操作,然后将哈希结果作为bit数组的索引,将索引位置的比特设置为1(不管原来是0还是1)
比如我们选取k=3,即三个哈希函数,对于数据A得到哈希值为0,5,7,那么比特数组就变为:
1000 0101 0000 0000 0000
数据B的哈希值为2,8,13,则添加后比特数组为:
1010 0101 1000 0100 0000
数据C的哈希值为0,4,7,则添加后的比特数组为:(0,7已经设置为1了,不用管,还是设置为1就可以了)
1010 1101 1000 0100 0000
3>查找
查找X是否在集合中,对X采用相同的3个哈希函数,然后根据3个哈希值,即索引去bit数组里找。如果3个索引对应的比特位都为1,就说明X在集合中,否则说明不在。
4>伪代码表示为:
public class BloomFilter
{
private bit[] bitSet = new bit[N];
public void add(Object element)
{
int[] hashValues = getHashValues(element);
for(int i : hashValues)
{
bitSet[i] = 1;
}
}
public boolean contains(Object element)
{
int[] hashValues = getHashValues(element);
for(int i : hashValues)
{
if(bitSet[i] != 1) return false;
}
return true;
}
}
2)优缺点
1>可以仅仅使用一位就代表一个数据
2>因为有多个哈希函数,所以可能会造成某一个数据不存在,但是他对应的三个位都被其他数据设置为1了,从而造成错误判断,但这种误差很低
3)与bit-Map的区别
bit-Map当所有数据中的最大数据不是很大的时候才适用,如果数据最大为15位(如qq号),那么也申请不到这么大的数组,所以,这个时候只能使用Bloom-Filter,因为Bloom-Filter可以将数据通过hash放在大小为n的位数组中