数据结构之bitmap和布隆过滤器
bitmap
问题:100个数,取值范围在0-2亿,如何快速的判断一个数是否在这100个数中?
思路1:开一个数组,数值对应数组的下标,对存在的100个数进行标记,比如设置为1;这样,查找一个数是否存在时,直接通过数组下面获取对应的值,判断是否为1即可。
这个思路非常简单高效,就是有一个问题,因为取值范围0-2亿,开个两个亿的空间数组有点不大科学,而且如果内存有限制的话,还开不出来。
思路2:既然我只是对存在的数做一个标记,那么用一个二进制位就可以了吧,0表示不存在,1表示存在,那么一个int类型中,每个元素都可以表示32个数字,这样空间对比开int数组,直接缩小32倍;在判断一个数是否存在时,只需要找到这个数对一个这个位是0还是1就知道了。
这种解决方案用到的就是bitmap,底层其实就是位数组。
使用bitmap,其实主要的就是数值和下标的映射关系,比如申请一个int数组,一个int元素有32位长,那么size为10的int数组可以表示320个数。
其实对应到内存中,比如如下32个位,可表示0-31共32个数字。
0 0 0 0 0000 0000 0000 0000 0000 0000 0000
31,30,29,28…
对于int数组,数字n 的位置位于 int[n / 32]中的 n % 32 这个位上。
有了这些,我们就可以自己实现一个bitmap了(需要有位运算的知识):
/**
* Bitmap
*
* 用位(bit)来表示一个数(元素)是否存在,相对于用boolean或者其他类型来表示一个元素是否存在,会节约大量的空间。
*
* 比如有100个数,取值范围在0-2亿,如何快速的判断一个数是否在这100个数中?
*
* 思路
* 申请一个int数组,每一个数组元素有4个字节,也就是32个bit,用二进制来表示的话,如下
* 0000 0000 0000 0000 0000 0000 0000 0000
* 那么这样一个32个bit就可以表示32个数是否存在。
* 比如
* 0000 0000 0000 0000 0000 0000 0000 0000 表示0
* 0000 0000 0000 0000 0000 0000 0000 0010 表示1
* 0000 0000 0000 0000 0000 0000 0000 0100 表示2
* 0000 0000 0000 0000 0000 0000 0000 1000 表示3
* 以此类推,int[0]就可以表示0-31,int[1]就可以表示32-63 ....
* 那么int[n]就可以表示n*32 到(n+1)*32-1 的数。
* 回到前面的问题, 范围在0-2亿的数,我需要开辟的int数组大小为 2亿/32 + 1 。
*
* 映射关系, 数字n 的位置位于 int[n / 32]中的 n % 32 位上。
*
*/
public class BitMap {
private int max;
private int[] bits;
public BitMap(int max) {
this.max = max;
bits = new int[max / 32 + 1];
}
/**
* 加入
* @param n
*/
public void add(int n){
int index = n >> 5; //定位到哪个 32 位的段上。
int loc = n & (32 - 1) ; //定位32位段上的哪个位 n % 32 的另外一种写法
bits[index] |= 1 << loc; //将loc的那个位置为1, 其他的位不变。
}
/**
* 移除
* @param n
*/
public void remove(int n){
int index = n >> 5;
int loc = n & (32 - 1) ;
bits[index] &= (1 << loc) - 1;