-
场景:
APP公告,在用户端展示需要红点标记未读
之前有看到类似的业务场景,比如用户标签,用户签到这种都可以使用位图实现。 -
原理
位图就是用一个二进制数据结构,按位存储标记,举个例子,假如APP有64个用户,用户ID为自增 , 一个long类型的数据刚好64位,那么作为byte数组使用,下标0~63
刚好能够表示这64个用户,每个下标对应的元素为1表示已读,0表示未读
public class BitMapTest {
public static void main(String[] args) {
BitMap bitMap = new BitMap();
bitMap.setBit(60);
System.out.println(bitMap.getBit(60));
System.out.println(bitMap.getBit(59));
}
static class BitMap{
byte[] k = new byte[64] ;
public void setBit(int id){
k[id-1] = 1;
}
public Boolean getBit(int id){
return k[id-1] == 1;
}
}
}
用点位运算可能看起来爽点
public class BitMapTest {
public static void main(String[] args) {
BitMap bitMap = new BitMap();
bitMap.setBit(60);
System.out.println(bitMap.getBit(60));
System.out.println(bitMap.getBit(59));
System.out.println(bitMap.getBit(64));
bitMap.setBit(64);
System.out.println(bitMap.getBit(64));
}
static class BitMap{
long a = 0;
public void setBit(long id){
a |= ( 1L << id - 1);
}
public Boolean getBit(long id){
return (a & (1L << id - 1)) != 0;
}
}
}
- 实现功能
一个APP的用户如果只有64个,那估计都得喝西北风去了,改造下~~
public class BitMapTest {
public static void main(String[] args) {
BitMap bitMap = new BitMap(10000000);
bitMap.setBit(999999);
System.out.println(bitMap.getBit(999999));
System.out.println(bitMap.getBit(59));
bitMap.setBit(1);
System.out.println(bitMap.getBit(1));
}
static class BitMap{
long[] ids ;
public BitMap(int size) {
this.ids = new long[calculateIndex(size-1) + 1];
}
public void setBit(int id){
ids[calculateIndex(id)] |= ( 1L << id - 1);
}
public Boolean getBit(int id){
return (ids[calculateIndex(id)] & (1L << id - 1)) != 0;
}
private int calculateIndex(int size){
return size >> 6;
}
}
}
这样子一个BitMap
对象就能容纳[1,9999999]
个用户ID了,回到我们最开始的需求,假设有9999999 个用户,一条公告需要记录所有用户的已读未读状态,不考虑边边角角的空间,需要内存大小为:
9999999 / 8 / 1024 = 1.1920MB
至少在能接受的范围了,当然造轮子主要为了理解原理,位图的实现在redis中也有,直接来用岂不快哉!