Bitmap位图
Bitmap是bit数组,只存储0和1,适用于存储整数类型数据
通过元素对应的位索引置1表示元素存在,后续通过位索引判断元素是否已存在
布隆过滤器
布隆过滤器是由一串长度固定的位数组和多组哈希函数组成。元素放入布隆过滤器中时,经过多组哈希函数运算得到多个位索引,将其置1。另外,布隆过滤器不支持删除操作
查询时判断多个索引对应的位置是否都为1,存在误判的可能。
数据去重
例如 对40亿个QQ号进行去重操作
如果直接存储QQ号,则需要40*4字节=15G,会消耗大量内存,在redis存储还会造成大key问题
bitmap实现
初始化bitmap长度大小为2^32
在变量QQ号时,将bitmap对应位索引的位置1,如果已经为1,则记录到日志中
内存使用:2^32*1bit/8字节=512Mb
//初始化长度为2 ^ 32位的位数组
BitSet bitmap = new BitSet(1L << 32); // 需调整JVM参数 -Xmx1g
//读取QQ号,如果该位为0,标记为1;否则数据重复
while(读取QQ号) {
if (!bitmap.get(qq)) {
//数据不存在才set 1,存在则去重了
bitmap.set(qq);
}
}
//最后,遍历Bitmap位数组,标记为1的位置就是去重后的结
布隆过滤器实现
使用公式 m = -ln p/(ln 2)^2计算位数组大小,例如:允许1%误判率时,1亿元素需要约 958 MB 空间(比Bitmap大,但支持任意数据)
通过上述公式初始化布隆过滤器,后面操作和bitmap类似
与bitmap相比
缺点:使用的内存大于bitmap,存在误判率,不适合对误判率要求0的场景
优点:不只是对整数进行去重,还可以对url等任何数据进行去重,适用场景多
bitmap其他使用场景
统计在线人数
笨方法:用redis的set集合存储在线用户,以业务标识为key,用户上线时将id存入set中,获取在线人数时直接获取set的大小。会产生大key问题
使用bitmap:使用redis的bitmap实现,以业务标识为key,用户上线就将用户id(一般使用的是雪花算法生成)作为偏移量存入bitmap中,下线则将对应位置0,极大节省了内存,如果数据太大,可以考虑对位图进行分片。
统计签到
使用bitmap:每个用户都分配一个独立的bitmap,然后以功能上线日期为起始索引,后续位索引=当前日期-功能上线日期=天数
统计连续签到天数:先获取起始日期的索引对应的十进制数,然后一次和后面的每一位进行与运算,同时计数器进行计数,一旦与得到0则结束
// 示例:假设当天是10月5日,dayOfMonth=5
// 签到数据二进制 → 10111(表示1号到5号的签到状态:1=已签,0=未签)
Long num = result.get(0); // 十进制值为23(二进制10111)
// 核心统计逻辑
int count = 0;
while (true) {
if ((num & 1) == 0) { // 检查最低位(对应当月第1天)
break;
} else {
count++; // 累计连续签到天数
}
num = num >> 1; // 右移1位(向高位移动)
}
// 最终count=3(错误结果,实际应统计最近连续签到)