一、介绍
1.1 什么是位图
在Java中有四种整数类型:byte、short、int、long。
1 byte(字节) = 8 bit(位)
1 int = 4 byte = 32 bit
所以我们可以通过一个int整数来表示32个数字!
如现在有一个整数0,他的二进制有32位。
打印二进制位:
/**
* 打印二进制
* 从0位开始到31位
* @param num
*/
public static void printNum(int num){
for (int i = 31; i >= 0; i--) {
System.out.print((num & (1<<i)) == 0 ? "0": "1");
}
System.out.println();
}
我们可以通过将32位上的第几位标记为1来表示数字有没有出现过。
例如:
00000000000000000000000000000000 来表示0
00000000000000000000000000000001 表示1出现
00000000000000000000000000000010 表示2出现
00000000000000000000000000000100 表示3出现
....
10000000000000000000000000000000 表示31出现
这样通过一个数字就可以表示32个数字。可以极大的节省内存空间。
这样在数组中:
第1个数字表示的范围为0 ~ 31;
第2个数字表示的范围为32 ~ 63;
第3个数字表示的范围为64 ~ 95;
第4个数字表示的范围为96 ~ 127;
第5个数字表示的范围为128 ~ 159;
…
依次类推
例如:
如果我想要表示92102
这个数字出现过,则用92102除以32
,找出在数组上的第几位数字。所以92102
这个数字在数组上第2878
个数的范围内,接下来找这个数字上的第几位,取92102模上32的余数
,即在数组上第2878
个数二进制的6
位,标记为1
。标志92102
这个数字出现过。
1.2 怎么标记
上面的思路有了,接下来就是怎么实现了标记和取消标记。
将一个整数的32位二进制上的第几位标记为1
将哪一位标记为1,可以让该位置和1| 或
操作。
(注意:从第0位开始,到第7位,所以标记在第八位)
1.3 怎么取消标记
将二进制位上的1设置为0:
如:00000000000000000000000010000000 第7位上已经标记为1了,现在要取消,即该位设置为0。
即让该位置和1
进行与 &
操作即可。
1.4 判断该位置是否标记
标记1 可以表示添加一个数字;
标记0 可以表示删除一个数字;
我们还需要 一个方法来 判断这个数字是否存在? 即是否标记1。
可以通过该位置上和1 &与操作
,如果都为1 则
二、代码实现
public static class BitMap{
public int[] bits;
/**
* 初始化数组的长度,最大值(补上一位) 除以32 即表示数组最大多少长度可以表示这个最大值。
* @param max 表示此位图结构中存储的最大的值
*/
public BitMap(int max){
//(max + 32) >> 5 等同于 (max + 32)/32
bits = new int[(max + 32)>>5];
}
/**
* 通过或操作
* 向位图里添加一个数
* 00000000000000000000000000000001
* 00000000000000000000000000010000
* 与操作
* 00000000000000000000000000010001 将该位置标记为1
* @param num
*/
public void add(int num){
//首先判断是在数组的哪一位上表示 除以32
//再判断这位整数上的第几位 模上32 取余数即表示第几位
//把这个位标记为1 即 与上1<<第几位
//bits[num >> 5] = bits[num >> 5] | 1<<(num % 32 ) 就等同于bits[num >> 5] |= 1<<(num % 32 )
//bits[num >> 5] = bits[num >> 5] | 1<<(num % 32 );
//注意:
//%64(除以64后的余数) 等同于 &63(与63) ,因为63的二进制表示为111111
//所以num % 32 等同于 num & 31
bits[num >> 5] |= 1<<(num & 31 );
}
/**
* 从位图里删除一个数
* 和(余数取反)与操作
*
* 该位置数:00000000000000000000000000100001
* 余数: 00000000000000000000000000000001
* 余数取反:11111111111111111111111111111110
* 与操作: 00000000000000000000000000000001
* 即该位置上的1消除,表示此数值已删除
* @param num
*/
public void del(int num){
//通过&与操作将数组上第几位的数字上的该位置清空
bits[num >> 5] &= ~(1<<(num & 31 ));
}
/**
* 查看位图中是否包含这个数
* @param targetNum 目标数
* @return 是否包含
*/
public boolean contains(int targetNum){
//该位置上的数 和32的余数 与操作如果不为0 则存在 否则不存在
return (bits[targetNum >> 5] & (1 << (targetNum & 31))) != 0;
}
}
三、测试
public static void main(String[] args) {
//新建一个最大值为1000的位图
BitMap bitMap = new BitMap(1000);
System.out.println("数组长度:"+bitMap.bits.length);
System.out.println("是否包含100:"+bitMap.contains(100));
//添加操作 :100/32 = 3...4 ;即把数组第4个元素的第4位标记为1
bitMap.add(100);
printNum(bitMap.bits[3]);
System.out.println("是否包含100:"+bitMap.contains(100));
//删除操作
bitMap.del(100);
System.out.println("是否包含100:"+bitMap.contains(100));
}