位图(Bitmap),即位(Bit)的集合,是一种数据结构,可用于记录大量的0-1状态,在不少地方都会用到,好比Linux内核(如inode,磁盘块)、Bloom Filter算法等,其优点是能够在一个很是高的空间利用率下保存大量0-1状态。html
BitMap的原理java
BitMap 的基本原理就是用一个bit 位来存放某种状态,适用于大规模数据,但数据状态又不是不少的状况。一般是用来判断某个数据存不存在的。node
举例:在Java里面一个int类型占4个字节,假如要对于10亿个int数据进行处理呢?10亿*4/1024/1024/1024=4个G左右,须要4个G的内存。 算法
若是可以采用bit储,10_0000_0000Bit=1_2500_0000byte=122070KB=119MB, 那么在存储空间方面能够大大节省。数组
在Java里面,BitMap已经有对应实现的数据结构类java.util.BitSet,BitSet的底层使用的是long类型的数组来存储元素。数据结构
咱们来看看具体存储:函数
对于1,3,5,7这四个数,若是存在的话,则能够这样表示:测试
1表明这个数存在,0表明不存在。例如表中01010101表明1,3,5,7存在,0,2,4,6不存在。那若是8,10,14也存在怎么存呢?如图,8,10,14咱们能够存在第二个字节里大数据
以此类推。spa
Map映射表
假设须要排序或者查找的总数N=10000000,那么咱们须要申请内存空间的大小为int a[1 + N/32],其中:a[0]在内存中占32为能够对应十进制数0-31,依次类推:
bitmap表为:
a[0]--------->0-31
a[1]--------->32-63
a[2]--------->64-95
a[3]--------->96-127
..........
BitMap算法处理大数据问题的场景:
(1)给定10亿个不重复的正int的整数,没排过序的,而后再给一个数,如何快速判断这个数是否在那10亿个数当中。
解法:遍历40个亿数字,映射到BitMap中,而后对于给出的数,直接判断指定的位上存在不存在便可。
(2)使用位图法判断正整形数组是否存在重复
解法:遍历一遍,存在以后设置成1,每次放以前先判断是否存在,若是存在,就表明该元素重复。
(3)使用位图法进行元素不重复的正整形数组排序
解法:遍历一遍,设置状态1,而后再次遍历,对状态等于1的进行输出,参考计数排序的原理。
(4)在2.5亿个整数中找出不重复的正整数,注,内存不足以容纳这2.5亿个整数
解法1:采用2-Bitmap(每一个数分配2bit,00表示不存在,01表示出现一次,10表示屡次,11无心义)。
解法2:采用两个BitMap,即第一个Bitmap存储的是整数是否出现,接着,在以后的遍历先判断第一个BitMap里面是否出现过,若是出现就设置第二个BitMap对应的位置也为1,最后遍历BitMap,仅仅在一个BitMap中出现过的元素,就是不重复的整数。
解法3:分治+Hash取模,拆分红多个小文件,而后一个个文件读取,直到内存装的下,而后采用Hash+Count的方式判断便可。
该类问题的变形问题,如已知某个文件内包含一些电话号码,每一个号码为8位数字,统计不一样号码的个数。8位最多99 999 999,大概须要99m个bit,大概10几m字节的内存便可。 (能够理解为从0-99 999 999的数字,每一个数字对应一个Bit位,因此只须要99M个Bit==12MBytes,这样,就用了小小的12M左右的内存表示了全部的8位数的电话)
BitMap的一些缺点:
(1)数据碰撞。好比将字符串映射到 BitMap 的时候会有碰撞的问题,那就能够考虑用 Bloom Filter 来解决,Bloom Filter 使用多个 Hash 函数来减小冲突的几率。
(2)数据稀疏。又好比要存入(10,8887983,93452134)这三个数据,咱们须要创建一个 99999999 长度的 BitMap ,可是实际上只存了3个数据,这时候就有很大的空间浪费,碰到这种问题的话,能够经过引入 Roaring BitMap 来解决。
例子:
从正整数数组中寻找重复的整数
import java.util.BitSet; import java.util.HashSet; import java.util.Set; public class TestBitMap { //假设数据是以数组的形式给咱们的 public static Set test(int[] arr) { int j = 0; //避免返回重复的数,存在Set里 Set output = new HashSet(); BitSet bitSet = new BitSet(Integer.MAX_VALUE); int i = 0; while (i < arr.length) { int value = arr[i]; //判断该数是否存在bitSet里 if (bitSet.get(value)) { output.add(value); } else { bitSet.set(value, true); } i++; } return output; } //测试 public static void main(String[] args) { int[] t = {1,2,3,4,5,6,7,8,3,4,9}; Set t2 = test(t); System.out.println(t2); } }
总结
本文主要介绍了BitMap算法的基本原理和应用案例,其本质上是采用了bit位来表示元素状态,从而在特定场景下可以极大的节省存储空间,很是适合对海量数据的查找,判重,删除等问题的处理。
其余参考:
http://www.javashuo.com/article/p-xachuatg-cs.html
https://www.cnblogs.com/gczr/p/7358813.html