一、位图概念
1、可以先了解下位运算
位运算
2、位图就是充分利用位运算,来进行数据的查找、去重
比如有一千万个数,范围为一到一亿,如果判断某个整数是否在其中,应该怎么做呢?
- 很显然,我们可以申请一个大小为一亿,数据类型为boolean的数组,把这一千万个数放在数组中,标志位为True。比如K这个数在其中,array(k)= true;判断某个数是否在其中,只需判断返回是否为true即可;
- 不过,很多语言中boolean大多占用1个字节,用true/false表示存不存在,并不能节省太多内存空间;
- 这时候就可以利用bit的特性,我们知道1个字节占用8个bit位,如果把这一亿个数全放在bit位中,存在就设置为1,不存在就设置为0;相当于1个字节可以表示8个数字;比如6可以表示成00000100
- 至于如何放在bit位中,不好描述清楚,请看代码实现
二、代码实现
public class BitMap { // Java中char类型占16bit,也即是2个字节
private char[] bytes;
private int nbits;
public BitMap(int nbits) {
this.nbits = nbits;
this.bytes = new char[nbits/16+1];
}
/*
把int所在的bit位置为1
*/
public void set(int k) {
if (k > nbits) return;
int byteIndex = k / 16;
int bitIndex = k % 16;
bytes[byteIndex] |= (1 << bitIndex);
}
/*
两个bit位进行与操作,若为1,表示两个bit都为1,即这个数字已经存在
*/
public boolean get(int k) {
if (k > nbits) return false;
int byteIndex = k / 16;
int bitIndex = k % 16;
return (bytes[byteIndex] & (1 << bitIndex)) != 0;
}
}
三、位图的优势
- 每个数字用一个二进制位表示,占用空间非常小,相当于以前一个int(4字节),现在能表示32个数
- 计算机本身是使用位运算的,使用位图的话可以充分利用这个特点,计算效率高
四、布隆过滤器
- 假设还是有1千万个数,数字的范围为1到10亿,数字范围比之前增大了10倍,即使用位图也会消耗不少的内存空间,这时候布隆过滤器就出场了,布隆过滤器还是使用1亿个bit位,只不过是对这1千万个数字进行哈希,比如哈希取模,f(x)
= y % n,n就是1亿,就可以保证1到10亿都落在这1亿个bit位中 - 但这种会有个问题,比如一和一亿零一同样进行哈希取模,都是落在1这个bit位,那么就无法判断存储的是一还是一亿零一
- 针对这种情况,可以使用多个哈希函数去定位一个数据,是否可以降低冲突的概率呢?答案是可以的,比如x经过三次哈希之后,在bit位1、3、9为true;y经过三次哈希之后,在bit位2、4、8为true。但这样又会带来一个新的问题,比如z经过三次哈希之后为1、3、8,而138的bit位又都为true,这就容易出现误判的情况,针对这种误判,只能尽可能提高位数组的长度并且使用尽可能多的哈希函数,但这也会带来存储空间的提升和性能的下降,因此这需要权衡
- 对于某个数据,如果经过了布隆过滤器判断不存在,那么它一定不存在;若判断存在,那是有可能存在,因此布隆过滤器适用于对误判容忍度比较高的场景
应用场景:
1、缓存穿透:当key对应的数据在数据源中并不存在,
2、爬虫网址去重
3、黑名单:不在黑名单数据,会被布隆过滤器过滤出来;可能存在的网址再去数据源查询,降低数据源压力