BitMap的原理以及運用


  

位圖(Bitmap),即位(Bit)的集合,是一種數據結構,可用於記錄大量的0-1狀態,在很多地方都會用到,比如Linux內核(如inode,磁盤塊)、Bloom Filter算法等,其優勢是可以在一個非常高的空間利用率下保存大量0-1狀態。

 

BitMap的原理

  BitMap 的基本原理就是用一個bit 位來存放某種狀態,適用於大規模數據,但數據狀態又不是很多的情況。通常是用來判斷某個數據存不存在的。

  舉例: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我們可以存在第二個字節里

    

   以此類推。

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位來表示元素狀態,從而在特定場景下能夠極大的節省存儲空間,非常適合對海量數據的查找,判重,刪除等問題的處理。

其他參考:

https://www.cnblogs.com/hongdada/p/8267032.html

https://www.cnblogs.com/gczr/p/7358813.html



 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM