数据结构精讲:从原理到实战–学习笔记02
本笔记是记录学习 《数据结构精讲:从原理到实战》,作者是:蔡元楠,Google Brain资深工程师。
如有侵权,联系删除!
位数组
位数组 (bit array) 也称为 位图 (bit map)
如果我们需要用一种数据类型来记录布尔值的数组,通常定义数组如下:
int[] d = new int[2];
这样的特点是:每个int型变量来储存一个布尔型变量,但是记录单个布尔值只需要1位(bit),而32和64位中,java的int型变量都是4字节(byte)= 8 * 4 = 32 位。因此,我们浪费了31/32的内存空间。
倘若我们把每个int型变量的32位来表示32个布尔值的数组,这种将每个元素中的每一个比特位都作为状态信息存储的数组称之为位数组(Bit Array)或者位图(Bit Map)
上述定义的数组d在内存中如图所示:
上述可以表示64个状态位的数组。
当我们要操作位数组中在位置为“i”这个比特位的时候,应该如何定位它呢?很简单,可以按照下面的公式来定位。
所在数组中的元素为: i / data_size
比特位在元素中的位置为:i % data_size
那我们以定位索引为 35 这个比特位为例子来说明一下,套用上面的公式,可以得出:
所在数组中的元素为: 35 / 32 = 1
比特位在元素中的位置为:35 % 32 = 3
所以这个比特位是位于 d[1] 这个元素上索引为 3 的位置
位数组的实现
一般来说因为位数组的元素只保存“0”或者“1”两种状态,所以对于位数组的操作有以下几种:
-
获取某个位置的比特位状态;
-
设置某个位置的比特位,也就是将那个位置的比特位设置为“1”;
-
清除某个位置的比特位,也就是将那个位置的比特位设置为“0”。
GetBit()
public boolean GetBit(int[] array, int index) {
int elementIndex = index / 32;
int position = index % 32;
long flag = 1;
flag = flag << position;
if((array[elementIndex] & flag) != 0) {
return true;
} else {
return false;
}
}
这里 & 运算可以这样理解:
偏移后的flag中1的位置即为所求位置,1和array[elementIndex] 经过 & 运算后,flag中那个1所在的位置对应array[elementIndex] 中的位置的值如果为1则返回真。如果觉得难以理解,可以参考下图:
当elementIndex为1时,flag偏移3位后(flag末尾几位为:00001000),能够检验图中第35位的值是否为1;若为1则返回true,反之返回false。
SetBit()
public void SetBit(int[] array, int index) {
int elementIndex = index / 32;
int position = index % 32;
long flag = 1;
flag = flag << position;
array[elementIndex] = (int) (array[elementIndex] | flag);
}
ClearBit()
public void ClearBit(int[] array, int index) {
int elementIndex = index / 32;
int position = index % 32;
long flag = 1;
flag = ~flag << position;
array[elementIndex] = (int) (array[elementIndex] & flag);
}
Redis中的Bitmap数据结构
Redis 是一个开源的并且使用内存来作为存储空间的高效数据库,感兴趣的可以到官网 https://redis.io 上查看相关文档。
Redis 里面的一个数据结构——BitmapBitmap 的本质其实是在 Redis 里面通过一个 Strings 类型来表示的。在 Redis 中,Strings 的最大长度可以是 512MB。
在 Redis 里面对 Bitmap 的操作命令有以下几种:BITCOUNT、BITFIELD、BITOP、GETBIT、SETBIT。其中,GETBIT 和 SETBIT 命令和前面我们自己所实现的 GetBit 和 SetBit 操作原理是一样的,感兴趣的同学可以前往 GitHub 链接来查看 Redis 中 Bitmap 的源码。
可以用 Redis 里的 BITCOUNT、SETBIT 和 BITOP 来完成。BITCOUNT 这个命令其实是可以计算一个位数组里有多少比特位是为“1”的,而 BITOP 可以针对位数组进行“与”、“或”、“非”、“异或”这样的操作。
Redis 的 Bitmap 操作之所以强大,是因为所有操作都是位运算以及发生在内存中,所以速度极快。
位数组这种数据结构可以极大地优化内存空间,当我们要表达的状态只有true和false时,便可以考虑使用这种数据结构。