基本介绍
本质上是哈希表的一种应用实现,原理简单,以 bit 为单位构建数组的方案,就叫作 Bitmap,翻译为位图。即bit 的集合;使用一个bit表示状态, 两种状态 (0不存在和1存在) 使用最少字节的类型来定义数组,即最小的空间存储数据标识。
位图适合对【数值类型】的海量数据进行查询统计、排序、去重 和 对两个集合做交集、并集运算;bitmap在数据连续的时候,非常节省空间,但是在数据稀疏的时候,会有极大的浪费,当出现hash碰撞时可能映射到同个位置;
业务应用
日活/月活UV统计、签到统计、用户点赞,用户签到,访问计数,在线用户数等
典型案例应用解析
1)找存在的UserId
一个32位4G内存的操作系统,在20亿个整数,找出某个数X是否存在其中,在Java环境中int占4个字节,一个字节占8位。
方式1:将用户id存到集合,那么需要消耗20亿*4*8=640亿位=640/8/1024/1024/1024=7.48GB
方式2:不存储具体数值,仅存储是否存在,存在1不存在0,20亿位/8/1024/1024/1024=0.23GB
解决方案:
1)使用数组处理
每个int类型可标识32个整数,存储20亿个元素需要20亿个比特位,20亿/8/1024/1024 约上200多MB,省32倍空间。需要申请的数组大小,总的数组长度为20亿/32 +1。
array[0]:可表示0~31,array[1]:可表示32~63,array[2]可表示64~95
如何确定位置(给定任意整数M,那么M/32就得到下标,M%32就知道它在此下标的哪个位置)
如元素存储 80,确定所在数组的bit位置
1、数组index索引 80/32 = 2.5,即第3个数组的位置 arr[2]
2、比特位index索引 80%32 = 16,索引下标为16的比特位,把比特位设置为1,即arr[2][16]
2)使用redis使用bitmap数据结构处理
Redis中提供的BitMap命令:setbit,getbit,bitcount检测用户是存在(checkUserIsExsits是Key):setbit checkUserIsExsits user-uid 101024511例子:setbit checkUserIsExsits 101024511是否存在判断:getbit checkUserIsExsits user-uid例子:getbit checkUserIsExsits 101024511如果不存在状态是0,如果已存在就是1统计当前有多少个用户bitcount checkUserIsExsits返回值为该key值中1的个数
2)找不存在的随机数
需求:有1千万个随机数,随机数的范围在1到1亿之间,将1到1亿之间没有在随机数中的数求出来
前提条件:使用java现有数据结构或自定义数据结构,要求高效和省空间
解决方案:遍历X亿个数字,映射到BitMap中,对于给出的数,直接判断指定的位上存在不存在即可
public static void testBitMap2(){//范围int range = 100;//个数int totalNum = 10;List<Integer> list=new ArrayList<>();//1.声明一个BitSetBitSet bitSet = new BitSet(range);//2.生成1千万个随机数for (int i = 0; i < totalNum; i++) {int random = (int) (Math.random() * range);bitSet.set(random);list.add(random);}System.out.println("产生的随机数:"+list);System.out.println("bitmap是1的个数(存在重复随机送):"+bitSet.cardinality());System.out.println("bitmap是size:"+bitSet.size());System.out.println("bitmap是length(bitmap里面占据的位数是1的位数+1):"+bitSet.length());//3.遍历BitSet,将没有出现的数打印出来for (int i = 0; i < range; i++) {if (!bitSet.get(i)) {System.out.print(i+",");}}}
该数据结构缺点
数据碰撞:字符串映射到 bitmap会有碰撞问题,即可能映射到同个位置,即hash碰撞
稀疏数据:不连续的数据容易浪费空间,比如存入1和88两个数,需要构建长度89的数组,表示索引从1到88,所以需要构建一个长度为89的数组,存放1到88的元素,但实际只存储2个数字。
如果用户的ID的数据类型是int32的话,那么最大值是2^32,需要用512MB的字节的位图来表示,2^32bit=4294967296 比特(bit)=512 兆字节(MB)。
如果只往bitmap存储一个最大值,那边需要申请512 兆字节(MB),大大浪费空间