bitMap原理及实战

小知识

  1. 在实际项目中,我们经常需要聚合统计,比如统计一个年龄在20-30,喜欢看技术书籍,喜欢听音乐,喜欢宅在家的程序员等等一系列标签的用户。
    如果使用mysql求并集,首先语句随着标签变长而变长,其次聚合,分组,去重严重影响语句性能。这种情况如何解决?
  2. 比如现在比较火的面试题,在10亿整数中找出100个重复的数,或者任意给定一个整数,判断是否在这个10亿数中。

bitMap原理

bitMap就是使用bit位来标记元素,key为该元素,value一般为0或者1,大大节省存储空间.

现在有(2, 6, 5, 7,6)5个数,任意给定一个在0-7范围内的数字,判断是否在此集合中:

  1. 创建一个范围为(0-7)的Byte类型数组,将集合数字对应数组的bit位置置1;
  2. 然后遍历该Byte数组,如果Byte数组位置为1即代表该数存在。
  3. 同理对于10亿整数也可以这样处理,一个int型数字4个字节,32bit,如果使用bit标记正整数,就可以节省32倍的内存空间。
  4. 10亿数字,40亿字节,320亿bit,需要大约4g内存,使用bitMap标记,只需要125M内存空间即可存储,大大节省内存空间。

以上和桶排序排序的思想非常相似。

bitMap实际运用

对于1千万数据,判断任意给定的数是否在其中?
分治思想
使用int数组作为bitMap。
将数组分成32组,每组内有(0-31)个位置,如果给定数组在指定数组中的bit是0,则不存在。

  1. 求十进制数在对应数组a中的下标
    a[i] = a[N/32]

  2. 求int[]中bit位置
    index = a[i] % 32

上述两个运算可以改成位运算,因为位运算的效率非常高,占用cpu的时钟周期非常少。
结论:对于2的倍数,%2^n = &(2^n-1),模运算等于与预算,例:a % 16 = a & 15,这里的15做与运算时需要化成16进制,即0x0F.

在10000000个范围为[1-100000]数中,给定指定一个数,判断是否在这个集合中

代码:

public class BitMapActual {
    //1千万数据集合
    private static final int N = 10000000;

    //bitmap数组
    private static int[] a = new int[N/32 + 1]; //int 等于32个bit 所以数据长度为(N/32+1)

    /**
     * 为集合数据加标记
     * @param n
     */
    public static void addValue(int n) {
        //定位数组编号 相当于n/32
        int row = n >> 5;

        //定位数组内slot位置 相当于n%32
        int offset = n & 0x1F;

        //数组slot置1
        a[row] |= 1 << offset;
    }

    /**
     * 判断给定数字是否存在
     * @param n
     * @return
     */
    public static boolean exits(int n) {
        //定位数组编号
        int row = n >> 5;

        //定位数组内slot位置
        int offset = n & 0x1F;

        // 1 << position 将a[i]中左移position求与,slot位置有值返回true
        return (a[row] & ( 1 << offset)) != 0;
    }

    public static void main(String[] args) {
        //初始化一个长度为N的数组
        int num[] = new int[N];
        Random random = new Random();
        for (int i = 0; i < N; i++) {
            //随机数范围是(0-100000)
            int item = random.nextInt(100000);
            num[i] = item;
        }

        BitMapActual map = new BitMapActual();
        //置1
        for(int i = 0; i < N; i++){
            map.addValue(num[i]);
        }

        int temp = 200;
        if(map.exits(temp)){
            System.out.println("temp:" + temp + "has already exists");
        } else {
            System.out.println("temp:" + temp + "has no exists");
        }
    }
}

注:本段代码有一些移位运算,感兴趣可以研究下

猜你喜欢

转载自blog.csdn.net/LJJZJ/article/details/84994653
今日推荐