天天看点

java | BitMap原理解析

概述

在一些数据量比较大的场景中,做一些查重、排序,一般的方法难以实现。数据量过大,会占用较大的内存,常用的处理方式有两种:BitMap(位图法)和布隆过滤。

本篇针对以下题目来看一下如何用位图法来实现:10亿个正整数,给定一个数值,如何快速排定该数值是否在10亿个正整数当中?

位图法的思想类似于hash寻址,首先初始化一个int数组,每个元素对应32位比特,将10亿个元素分别读入内存,对int数组中的元素比特位进行标记,如果存在,标记为1即可。标记完之后,即可快速判定某个元素是否在10亿个正整数当中,时间复杂度为O(1)。

需要多大的内存?

10亿个元素需要10亿个比特位,10亿/8/1024/1024 = 120 M,相比原来节省了 32 倍内存。

注意:这是最小使用内存。原因后面会介绍。

需要申请多大的数组?

array[0]:可表示0~31

array[1]:可表示32~63

array[2]可表示64~95

数组长度为10亿/32 +1即可。

每个int类型可标识32个整数,如下图:

java | BitMap原理解析

寻址

比如元素 119,如何确定其对应的数组比特位 index ?

1)确定数组 index:119/32 = 3.7,也就是第 4 个数组元素,a[3] 的位置。

2)确定比特位 index:119%32 = 23,第23个比特位。

3)将比特位设置为1。

设置比特位

1)将比特位设置为1

java | BitMap原理解析

将第28个比特位设置为1:

只需将 1 左移(31-28)位数,然后与原来的值进行或运算。

2)将比特位设置为0

java | BitMap原理解析

将第28个比特位设置为0:

只需将 1 左移(31-28)位数,并进行非运算,然后与原来的值进行与运算。

3)判断某一元素是否存在

java | BitMap原理解析

判断 28 位比特位是否有元素存在:

只需将 1 左移(31-28)位数,然后与原来的值进行与运算。只要与运算结果中有1,即表示元素存在。所以可以用运行结果是不为0作为元素是否存在依据。

实现

public class BigMapTest {

    private int[] bigArray;

    public BigMapTest(long  size){
        bigArray = new int[(int) (size/ 32 + 1)];
    }

    public void set1(int  num){
        //确定数组 index
        int arrayIndex = num >> 5;
        //确定bit index
        int bitIndex = num & 31;
        //设置0
        bigArray[arrayIndex] |= 1 << bitIndex;
    }

    public void set0(int  num){
        //确定数组 index
        int arrayIndex = num >> 5;
        //确定bit index
        int bitIndex = num & 31;
        //设置0
        bigArray[arrayIndex] &= ~(1 << bitIndex);
        System.out.println(get32BitBinString(bigArray[arrayIndex]));
    }

    public boolean isExist(int  num){
        //确定数组 index
        int arrayIndex = num >> 5;
        //确定bit index
        int bitIndex = num & 31;

        //判断是否存在
        return (bigArray[arrayIndex] & ((1 << bitIndex)))!=0 ? true : false;
    }

    /**
     * 将整型数字转换为二进制字符串,一共32位,不舍弃前面的0
     * @param number 整型数字
     * @return 二进制字符串
     */
    private static String get32BitBinString(int number) {
        StringBuilder sBuilder = new StringBuilder();
        for (int i = 0; i < 32; i++){
            sBuilder.append(number & 1);
            number = number >>> 1;
        }
        return sBuilder.reverse().toString();
    }
    
    public static void main(String[] args) {

        int[] arrays = new int[]{1, 2, 35, 22, 56, 334, 245, 2234, 54};

        BigMapTest bigMapTest = new BigMapTest(2234-1);

        for (int i : arrays) {
            bigMapTest.set1(i);
        }
        System.out.println(bigMapTest.isExist(35));
    }
}

           

BitMap 优缺点

优点:实现简单。适合数据量比较大的场景。

缺点:占用内存。申请的数组长度不好控制和最大的数值有关。当某个值特别大的情况下,映射的数组超过申请的数组容量,会出现下标越界。这也是上面提到的10亿个元素占用120M是最小内存的原因,实际可能会大于这个内存。