位圖(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