JAVA实现位图

概述
JAVA中一个int占4字节,32位二进制数, 如果一个int整数32位二进制都表示一个数的话,那么一个整数能表示[0,31]
如果想表示[32,64]可以加入数组,让索引0位置的数表示[0,31],索引1表示[32,64],以此类推只要知道要存入的最大值,就可以实现一个比原来数组占用空间至少小32倍的数组 ,就是位图
位图的实现
1 . 初始化数组大小

假设N就是位图中的最大值,那么数组的长度就应该是N / 32 + 1
 等同于 (N + 1) >> 5

2 . 加入元素

a . N >> 5 找到索引值
 b . N % 32 余数就是索引值所在32位的具体位置,等同于N & 31
 c . 让1 左移N &31位和数组索引位置值得二进制做个 | 运算

可能描述起来还不是特别清楚,直接上图吧!!

java 位图存字符串 java 位图类_java 位图存字符串


3 . 删除元素

删除元素和增加元素类似,在处理结果时让索引位置的值 & ~(1 << (N & 31))
就把这个位置上设置成0了

4 . 判断元素是否存在

这个是和add差不多的操作,查看N的索引N的位置的这个值是否为1
直接 (arr[val >> 5] & (1 << (val & 63))) != 0 即可

代码如下(带对数器)

package bitmap;

import java.util.HashSet;

/**
 * @Describe 实现位图
 * @Author 王超
 * @Version V1.0.0
 * @Date 2021/11/3 11:10
 */
public class BitMap {

    /**
     * 定义一个int数组,一个整数能表示32个数
     */
    private int[] arr;

    public BitMap(int max) {
        // 0 ~31
        // 32 ~ 63
        // 64 ~ 127
        // max / 32 + 1 = 数组长度
        arr = new int[(max + 32) >> 5];
    }

    /**
     * 1 . val >> 5 是因为找到索引位置 0~31是第0个索引位置,32~63是第一个索引位置 ,, 所以 /32 就是索引位置
     * 2 .  索引位置找到了,就应该找当前数在当前索引上处于32位的第几位就 =  val & 63 = val % 64
     * 3 . 1 << 是为了把这个二进制为无论0 1 都变成1
     * 4 . arr |= 是因为 ==> 如果之前这个索引位置上32位某一位被标记成1了,再来个数也在这个索引上,不仅这个数的32位对应的位要标称1还要不能改变之前的32位标记情况. 所以用|运算 arr[val << 5] |= 1 << (val & 63);
     * 例子 :0000 0000 0000 0000 0000 0000 0000 0000
     * add 个 0 变成 0000 0000 0000 0000 0000 0000 0000 0001
     * add 个 1 变成 0000 0000 0000 0000 0000 0000 0000 0011
     * add 个 2 变成 0000 0000 0000 0000 0000 0000 0000 0111
     * 例子过程 :
     * 1 . 加入0 ==> 先找到索引位置 == > 0 / 32 == 0
     * 2 . 锁定自己在32位的哪一位 == > 0 % 32 == 0
     * 3 . 把自己所在位标记成1 == > 000 ... 0001(1) << 0(val所在32位的位置) 得到 0000 0000 0000 0000 0000 0000 0000 0001
     * 4 . 0000 0000 0000 0000 0000 0000 0000 0001 | 0000 0000 0000 0000 0000 0000 0000 0000 = 0000 0000 0000 0000 0000 0000 0000 0001
     * 5 . 加入0 ==> 先找到索引位置 == > 1 / 32 == 0
     * 6 . 锁定自己在32位的哪一位 == > 1 % 32 == 1
     * 7 . 把自己所在位标记成1 == > 000 ... 0001(1) << 1(val所在32位的位置) 得到 0000 0000 0000 0000 0000 0000 0000 0010
     * 8 . 0000 0000 0000 0000 0000 0000 0000 0010 | 0000 0000 0000 0000 0000 0000 0000 0001 = 0000 0000 0000 0000 0000 0000 0000 0011
     *
     * @param val val
     */
    public void add(int val) {
        arr[val >> 5] |= 1 << (val & 63);
    }

    /**
     * 1 . val >> 5 是因为找到索引位置 0~31是第0个索引位置,32~63是第一个索引位置 ,, 所以 /32 就是索引位置
     * 2 .  索引位置找到了,就应该找当前数在当前索引上处于32位的第几位就 =  val & 63 = val % 64
     * 3 . 1 << 是为了把这个二进制为无论0 1 都变成1
     * 4 . ~取反是为了只把删除这个位变成0,其他位都是1
     * 4 . arr &= 是因为 ==> 原来位上可能有很多1 & 只有删除位是0其他位都是1 = 其他位是1还是1只有删除位变成0了
     * 例子 :0000 0000 0000 0000 0000 0000 0000 0000
     * add 个 0 变成 0000 0000 0000 0000 0000 0000 0000 0001
     * add 个 1 变成 0000 0000 0000 0000 0000 0000 0000 0011
     * add 个 2 变成 0000 0000 0000 0000 0000 0000 0000 0111
     * 例子过程 :
     * 1 . 加入0 ==> 先找到索引位置 == > 0 / 32 == 0
     * 2 . 锁定自己在32位的哪一位 == > 0 % 32 == 0
     * 3 . 把自己所在位标记成1 == > 000 ... 0001(1) << 0(val所在32位的位置) 得到 0000 0000 0000 0000 0000 0000 0000 0001
     * 4 . 0000 0000 0000 0000 0000 0000 0000 0001 | 0000 0000 0000 0000 0000 0000 0000 0000 = 0000 0000 0000 0000 0000 0000 0000 0001
     * 5 . 加入0 ==> 先找到索引位置 == > 1 / 32 == 0
     * 6 . 锁定自己在32位的哪一位 == > 1 % 32 == 1
     * 7 . 把自己所在位标记成1 == > 000 ... 0001(1) << 1(val所在32位的位置) 得到 0000 0000 0000 0000 0000 0000 0000 0010
     * 8 . 0000 0000 0000 0000 0000 0000 0000 0010 | 0000 0000 0000 0000 0000 0000 0000 0001 = 0000 0000 0000 0000 0000 0000 0000 0011
     *
     * @param val val
     */
    public void delete(int val) {
        // 找到他把他改成0
        arr[val >> 5] &= ~(1 << (val & 31));
    }

    /**
     * 判断val是够存在位图中
     * 思路是: 1 << 到val的位上,用索引的这个值做 & 运算,这样除了val位上是1,其他位置全变成0了
     * 如果1这个位对应的也是1就返回非0数,否则返回0
     *
     * @param val val
     * @return Boolean
     */
    public boolean contains(int val) {
        return (arr[val >> 5] & (1 << (val & 63))) != 0;
    }

    public static void main(String[] args) {
        System.out.println("测试开始!");
        int max = 10000;
        BitMap bitMap = new BitMap(max);
        HashSet<Integer> set = new HashSet<>();
        int testTime = 10000000;
        for (int i = 0; i < testTime; i++) {
            int num = (int) (Math.random() * (max + 1));
            double decide = Math.random();
            if (decide < 0.333) {
                bitMap.add(num);
                set.add(num);
            } else if (decide < 0.666) {
                bitMap.delete(num);
                set.remove(num);
            } else {
                if (bitMap.contains(num) != set.contains(num)) {
                    System.out.println("异常!");
                    break;
                }
            }
        }
        for (int num = 0; num <= max; num++) {
            if (bitMap.contains(num) != set.contains(num)) {
                System.out.println("异常!");
            }
        }
        System.out.println("测试结束!");
    }


}