BitMap
前言
Bitmap的基本思想就是用一个bit位来标记某个元素对应的Value,而Key即是该元素。由于采用了Bit为单位来存储数据,因此在存储空间方面,可以大大节省。
原理
bitmap就是使用bit位的值0或1标识一个数是否存在,0表示存在,1则表示不存在
如何使用bitmap表示一个数?
根据上述的实现原理可知,每一位表示一个数,0表示不存在,1表示存在。例如:{1,2,4,6}这几个数怎么在bitmap中表示?
计算机内存分配的最小单位是字节,也就是8位,那如果要表示{12,13,15}怎么办呢?当然是在另一个8位上表示了: 1个int占32位,那么只需要申请一个int数组长度为 int tmp[1+N/32] 即可存储,其中N表示要存储的这些数中的最大值,于是乎:
tmp[0]:可以表示0-31
tmp[1]:可以表示32-63
tmp[2]:可以表示64~95
给定任意整数M,那么M/32就得到下标,M%32就知道它在此下标的哪个位置
bitmap操作
添加元素
怎么把一个数添加到bitmap中呢?例如,想把5这个数字添加到bitmap? 首先,5/32=0,5%32=5,也是说它应该在tmp[0]的第5个位置,那我们把1向左移动5位,然后按位或: 相当于:86 | (1<<5) = 118 ---> b[0] = b[0] | (1<<5)
也就是说,要想插入一个数,将1左移带代表该数字的那一位,然后与原数进行按位或操作 化简一下,就是 86 + (5/8) | (1<<(5%8)) 因此,公式可以概括为: p + (i/8)|(1<<(i%8)) 其中,p表示现在的值,i表示待插入的数.
删除元素
通过上面的章节知道如何向bitmap中添加元素,那么如何删除一个元素呢?我们只需要执行和添加元素相反的操作就可以了。例如从bitmap中删除元素6,首先,6/32=0,6%32=6,也是说它应该在tmp[0]的第6个位置,那我们把1向左移动5位,然后按位与
查找元素
bitmap中每一位代表一个数字,1表示有(或者说存在),0表示无(或者说不存在)。通过把该位设置为1或者0来达到添加和清除的目的,那么判断一个数存不存在就是判断该数所在的位是0还是1 假设,我们想知道3在不在,那么只需判断 b[0] & (1<<3) 如果这个值是0,则不存在,如果是1,就表示存在。
Bitmap的使用场景
快速排序
根据Bitmap实现的原理可知,要想对一组数字进行排序时只需要将待排序的数字添加到bitmap中即可,然后遍历一遍bitmap获取的数据就是排序完成后的结果,时间复杂度为O(n)。
要求排序的数据不能存在重复值以及在大量密集数据情况下才能体现出来优势
快速去重
根据bitmap实现原理可以简单实现数据的去重,只需要将数据添加到bitmap中,相同的元组自然不会被重复添加到bitmap中,添加完成后,遍历bitmap取出数据就是去重后的结果。
快速查找
int数组中的一个元素是4字节占32位,那么除以32就知道元素的下标,对32求余数(%32)就知道它在哪一位,如果该位是1,则表示存在。
postgresql中的Bitmapset
c
typedef struct Bitmapset
{
int nwords; /* number of words in array */
bitmapword words[FLEXIBLE_ARRAY_MEMBER]; /* really [nwords] */
} Bitmapset;
postgresql中定义了相关的Bitmapset操作函数。bitmapset.h和bitmapset.c
查看Bitmapset中的元素
Bitmapset在内核代码中经常使用到,通过gdb调试程序时可能需要查看bitmapset内容,经过前面章节介绍应该了解bitmap实现的原理了,那么我们就可以知道如何查看bitmapset中的元素了。下面通过调试一个简单的UPDATE语句分析如果查看bitmapset中的元素: update t1 set a = 2;
- 堆栈信息:
- 添加完需要UPDATE权限的列 attno
相应的数字添加到bitmapset中,直接查看words的值不能直观的体现出来,那么可以通过gdb二进制格式显示:
从右到左数1是第8位(0开始),那么数字8被添加到bitmapset
附录
位运算
在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方,右移一位相当于除2,右移n位相当于除以2的n次方。
- << 左移,相当于乘以2的n次方,例如:1<<6 相当于1×64=64,3<<4 相当于3×16=48
- <math xmlns="http://www.w3.org/1998/Math/MathML"> > > >> </math>>> 右移,相当于除以2的n次方,例如:64>>3 相当于64÷8=8
- ^ 异或,相当于求余数,例如:48^32 相当于 48%32=16