Java位图法分析

位图法

Posted by changechenhao on March 8, 2019

前言

前几天的面试有问到海量数字去重的问题,当时知道能用位图法来解决,但却对其原理不是太了解所有有了这篇文章。

什么是位图法?

位图法即bitmap,就是使用每一个bit来存储数据状态,来表示数据是否存在,适合用于大规模的数据去重。 由上图可以知道,每一 bit 位代表一位数字,bit置为1表示数据存在。上图数组是从0开始的,可能出 现数字最小值很大的问题,那么小于最小值的bit位就空置了,所以我们需要关注最大数与最小数,这样 方便设置数组的大小。除了最大值与最小值的问题,我们还需关注数据是否有负数,如果有我们就需要使 用2个 bit 来存储数据。

位图法解决什么样的问题?

  • 给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在 那40亿个数当中?
    首先,将这40亿个数字存储到`bitmap`中,然后对于给出的数,判断是否在 `bitmap` 中即可。
    
  • 使用位图法判断整形数组是否存在重复
  遍历数组,一个一个放入 `bitmap`,并且检查其是否在 `bitmap` 中出现过,如果没出现放入,否则即为重复的元素。
  • 使用位图法进行整形数组排序
首先遍历数组,得到数组的最大最小值,然后根据这个最大最小值来缩小`bitmap` 的范围。这里需要注意对于int的负数,都要转化为 unsigned int 来处理,而且取位的时候,数字要减去最小值。
  • 在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数
采用2-`bitmap`(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)。其实,这里可以使用两个普 通的`bitmap`,即第一个`bitmap`存储的是整数是否出现,如果再次出现,则在第二个`bitmap`中设置即可。这样的话,就可以使用简单的1- `bitmap`了。

BitSet源码分析

set源码

//BitSet 使用 long 数组存储bit位,一个long可以存64位
public void set(int bitIndex) {
        if (bitIndex < 0)
            throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);
        //获取数组下标
        int wordIndex = wordIndex(bitIndex);
        //如果数组长度小了,扩容
        expandTo(wordIndex);
        //1L << bitIndex 是设置该 long 元素的位置,与 words[wordIndex] 
        //或运算是为了保留前面设置的数 
        words[wordIndex] |= (1L << bitIndex); // Restores invariants
    
        checkInvariants();
}
private static int wordIndex(int bitIndex) {
    //bitIndex 除以 64,即获取long数组的下标
    return bitIndex >> 6;
}

总结

参考