目录
一.位图(BitSet)
1.概念
用于存储数据的某种状态,常用于判断数据是否存在。但是,位图使用的场景有限,只适用于存储的数据类型是整数,并且这些整数没有重复的。如果这些数据重复了,那么无法确定这个数据是否一定出现,只能判断可不可能出现。
位图可以用于快速查询某一元素是否在集合中。
在Java中有BitSet类,我们可以实例化对象直接使用。
2.模拟实现
要完成模拟实现我们首先要了解其原理。
位图,顾名思义,是与位(bit)有关的。没错,我们就是将数据存储到位上,原理用到了哈希的思想。将一个byte分为8个位,每一个位可以存储一个数据。我们开一个byte的数组,使用哈希函数对一个数据进行处理后放入数组。这样byte数组中的一个位置可以存储8个数据,这不比直接存储好很多。
通过上面分析可以得知,我们要对一个数据哈希两次才能找到位置,第一次是找到数据要放在的数组下标,第二次是找到数据要存的位的下标。这里我们可以得到两个函数,arrIndex = val / 8,bitIndex = val % 8。这样我们就可以得到数据存储的具体位置了。
例子:我们要存储9,经过计算,arrIndex是1,bitIndex是1
原理明白了,可以模拟实现了。
2.1 set操作
这个操作的难点在怎么将某一个比特位的数据变成 1 ,而不改变其他的。这里我们要用到一个运算符——或(|)。或操作是 0 | 0 = 0,0 | 1=1,1 | 1=1,这个正好符合我们的需求:让某一变成1,其他位不影响。
具体操作:让1左移bitIndex位,得到的值与words[arrIndex]进行或操作。
//核心代码
int arrIndex=val/8;
int bitIndex=val%8;
word[arrIndex]|=(1<<bitIndex);
2.2 get操作
获得下标的思路与上述相同,这里着重说一下怎么获取。判断一个是否存在就是判断其应在的位置上的位是不是1。那么怎么判断是不是1?
这里我们也要用到一种运算符—— 与(&)。与操作可以保证只有 1 & 1=1,其他都不能得到1。这样就可以判断某一位是不是1。
//核心代码
int arrIndex=val/8;
int bitIndex=val%8;
if((word[arrIndex] & (1<<bitIndex))!=0){
return true;
}else{
return false;
}
2.3 reset操作
这个操作是将放入位图的元素删掉。
首先我们要知道我们要做到一个怎样的效果?是让一个byte中的某个1消失,其余的1和0都存在。
看过上面的操作的都知道还剩一个(^),但用过发现是不对的。这里我们要进行两次操作,一个是(&)一个是(~)。
//核心代码
int arrIndex=val/8;
int bitIndex=val%8;
word[arrIndex] &= ~(1<<bitIndex);
3.BitSet类
BitSet类有两个实例化方法:
BitSet bitSet1= new BitSet();
//规定大小
BitSet bitSet2= new BitSet(10);
BitSet中常用的方法:
方法 | 作用 |
void set(int index) | 将指定索引处的位设置为 true |
boolean get(int index) | 返回指定索引处的位值 |
void clear( ) | 将此 BitSet 中的所有位设置为 false |
void and(BitSet set) | 对此目标位 set 和参数位 set 执行逻辑与操作 |
void or(BitSet bitSet) | 对此位 set 和位 set 参数执行逻辑或操作 |
void xor(BitSet bitSet) | 对此位 set 和位 set 参数执行逻辑异或操作 |
位图还有个很厉害的方面就是就可以进行集合的并、交集操作(上面的方法有)。
二.布隆过滤器(Bloom Filter)
1.概念
第一眼看到布隆过滤器的人可能反应的是这个布隆:
不对嗷,不是这个⬆。
布隆过滤器(Bloom Filter)是1970年由Burton Howard Bloom提出的。其使用多个哈希函数将数据映射到位图中,以实现快速查询某个元素一定不存在或者可能存在。
为什么是查询某个元素一定不存在或者可能存在,而不是元素是否存在?这要从布隆过滤器的原理说起。
2.原理
举一个例子:Blom,这个字符串通过三个哈希函数得到了图上的三个位置,将图上的位置的比特位置成1。如果我们想要查找Blom就通过哈希得到位置后检测三个比特位是不是都是1,如果是那么说明可能存在,如果不是说明一定不存在。可能存在是因为有可能有其他字符串可以通过哈希函数得到相同的位置,这时就存在冲突了,因此无法判断某一个数据一定存在,只能判断可不可能存在。
这个特点也导致布隆过滤器无法正常删除元素,如果贸然让某个比特位变成0,可能会导致一些数据的丢失。如果Blom和Clom都是1,3,5,那么删掉Bolm这个标记的同时也删除了Colm的标记。
3.Bloom Filter的包
google的guava包中为我们实现了布隆过滤器,我们使用的时候直接调用即可。
在idea上建立一个pom包:
将下列代码复制进去:
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
</dependencies>
然后调用包,初始化:
private static int size = 1000000;//预计要插入多少数据
private static double fpp = 0.01;//期望的误判率
private static BloomFilter<Integer> bloomFilter=
BloomFilter.create(Funnels.integerFunnel(), size, fpp);
4.作用
网页URL的去重,垃圾邮件的判别,集合重复元素的判别,查询加速、数据库防止查询击穿, 使用BloomFilter来减少不存在的行或列的磁盘查找。
5.优缺点
优点:
使用同一组散列函数的布隆过滤器可以进行交、并、差运算;
布隆过滤器存储空间和插入/查询时间都是常数;
Hash函数相互之间没有关系,方便由硬件并行实现;
布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势;
布隆过滤器可以表示全集,其它任何数据结构都不能。
缺点:
随着存入的元素数量增加,误算率随之增加;
般情况下不能从布隆过滤器中删除元素;
不能获取元素本身。