日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

BitMap的原理和实现

發布時間:2025/3/18 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 BitMap的原理和实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

相關概念

 基礎類型

 在java中:  

byte -> 8 bits -->1字節 char -> 16 bit -->2字節 short -> 16 bits -->2字節 int -> 32 bits -->4字節 float -> 32 bits -->4字節 long -> 64 bits -->8字節

 位運算符

  在java中,int數據底層以補碼形式存儲。int型變量使用32bit存儲數據,其中最高位是符號位,0表示正數,1表示負數,可通過Integer.toBinaryString()轉換為bit字符串,

// 若最高的幾位為0則不輸出這幾位,從為1的那一位開始輸出 System.out.println(Integer.toBinaryString(10)); System.out.println(Integer.toBinaryString(-10)); // 會輸出(手工排版過,以下的輸出均會被手工排版):1010 11111111111111111111111111110110

 左移<<

  例如:5 << 2 = 20

首先會將5轉為2進制表示形式: 0000 0000 0000 0000 0000 0000 0000 0101 然后左移2位后,低位補0: 0000 0000 0000 0000 0000 0000 0001 0100 換算成10進制為20

 右移>>

  例如:?5 >> 2 = 1

還是先將5轉為2進制表示形式: 0000 0000 0000 0000 0000 0000 0000 0101 然后右移2位,高位補0: 0000 0000 0000 0000 0000 0000 0000 0001 換算成十進制后是1

 無符號右移>>>

   5 >>> 3    

  我們知道在Java中int類型占32位,可以表示一個正數,也可以表示一個負數。正數換算成二進制后的最高位為0,負數的二進制最高為為1。對于2進制補碼的加法運算,和平常的計算一樣,而且符號位也參與運算,不過最后只保留32位

-5換算成二進制: 1111 1111 1111 1111 1111 1111 1111 1011 -5右移3位: 1111 1111 1111 1111 1111 1111 1111 1111 // (用1進行補位,結果為-1) -5無符號右移3位: 0001 1111 1111 1111 1111 1111 1111 1111 // (用0進行補位,結果536870911 )

 位與&

  第一個操作數的的第n位于第二個操作數的第n位如果都是1,那么結果的第n為也為1,否則為0

5轉換為二進制:0000 0000 0000 0000 0000 0000 0000 0101 3轉換為二進制:0000 0000 0000 0000 0000 0000 0000 0011 ------------------------------------------------------------ 1轉換為二進制:0000 0000 0000 0000 0000 0000 0000 0001

 位或|

  第一個操作數的的第n位于第二個操作數的第n位只要有一個為1則為1,否則為0

5轉換為二進制:0000 0000 0000 0000 0000 0000 0000 0101 3轉換為二進制:0000 0000 0000 0000 0000 0000 0000 0011 ------------------------------------------------------------------------------------- 6轉換為二進制:0000 0000 0000 0000 0000 0000 0000 0111

  對于移位運算,例如將x左移/右移n位,如果x是byte、short、char、int,n會先模32(即n=n%32),然后再進行移位操作。可以這樣解釋:int類型為32位,移動32位(或以上)沒有意義。

  同理若x是long,n=n%64。

 左移和右移代替乘除

a=a*4; b=b/4;

 可以改為

a=a<<2; b=b>>2;

  說明:?  除2 = 右移1位 乘2 = 左移1位   除4 = 右移2位 乘4 = 左移2位   除8 = 右移3位 乘8 = 左移3位   … …

  類比十進制中的滿十進一,向左移動小數點后,數字就會縮小十倍,在二進制中滿二進一,進行右移一次相當于縮小了2兩倍,右移兩位相當于縮小了4倍,右移三位相當于縮小了8倍。通常如果需要乘以或除以2的n次方,都可以用移位的方法代替。

  實際上,只要是乘以或除以一個整數,均可以用移位的方法得到結果如:

  a=a*9

  分析a9可以拆分成a(8+1)即a8+a1, 因此可以改為: a=(a<<3)+a

  a=a*7

  分析a7可以拆分成a(8-1)即a8-a1, 因此可以改為: a=(a<<3)-a

  關于除法讀者可以類推, 此略。

  【注意】由于+/-運算符優先級比移位運算符高,所以在寫公式時候一定要記得添加括號,不可以 a = a*12 等價于 a = a<<3 +a <<2; 要寫成a = (a<<3)+(a <<2 )。

 與運算代替取余

31轉換為二進制:011111,0,31 32轉換為二進制:100010 與31取交集的結果是:10轉換為十進制為2 31轉換為二進制:100001 與31取交集的結果是:01轉換為十進制為1 30轉換為二進制:011110 與31取交集的結果是:11110轉換為十進制為30 29轉換為二進制:011101 與31取交集的結果是:11101轉換為十進制為29 33轉換為二進制:100001 與31取交集的結果是:1轉換為十進制為1

  31轉換為二進制后,低位值全部為1,高位全為0。所以和其進行與運算,高位和0與,結果是0,相當于將高位全部截取,截取后的結果肯定小于等于31,地位全部為1,與1與值為其本身,所以相當于對數進行了取余操作。

 進制轉換

  • 0x開頭表示16進制,例如:0x2表示:2,0x2f表示48
  • 0開頭表示8進制,例如:02表示:2,010表示:8
Integer.toHexString(int i) // 十進制轉成十六進制 Integer.toOctalString(int i) // 十進制轉成八進制 Integer.toBinaryString(int i)// 十進制轉成二進制 Integer.valueOf(m,n).toString() // 把n進制的m轉換為10進制

BitMap實現原理  

  在java中,一個int類型占32個字節,我們用一個int數組來表示時未new int[32],總計占用內存32*32bit,現假如我們用int字節碼的每一位表示一個數字的話,那么32個數字只需要一個int類型所占內存空間大小就夠了,這樣在大數據量的情況下會節省很多內存。

 具體思路:

  1個int占4字節即4*8=32位,那么我們只需要申請一個int數組長度為 int tmp[1+N/32]即可存儲完這些數據,其中N代表要進行查找的總數,tmp中的每個元素在內存在占32位可以對應表示十進制數0~31,所以可得到BitMap表:

    tmp[0]:可表示0~31

    tmp[1]:可表示32~63

    tmp[2]可表示64~95

    .......

  那么接下來就看看十進制數如何轉換為對應的bit位:

  假設這40億int數據為:6,3,8,32,36,......,那么具體的BitMap表示為:

  如何判斷int數字在tmp數組的哪個下標,這個其實可以通過直接除以32取整數部分,例如:整數8除以32取整等于0,那么8就在tmp[0]上。另外,我們如何知道了8在tmp[0]中的32個位中的哪個位,這種情況直接mod上32就ok,又如整數8,在tmp[0]中的第8 mod上32等于8,那么整數8就在tmp[0]中的第八個bit位(從右邊數起)。

BitMap源碼

private long length;private static int[] bitsMap;private static final int[] BIT_VALUE = {0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020,0x00000040, 0x00000080, 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000,0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, 0x00800000,0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000};public BitMap2(long length) {this.length = length;/*** 根據長度算出,所需數組大小* 當 length%32=0 時大小等于* = length/32* 當 length%32>0 時大小等于* = length/32+l*/bitsMap = new int[(int) (length >> 5) + ((length & 31) > 0 ? 1 : 0)];}/*** @param n 要被設置的值為n*/public void setN(long n) {if (n < 0 || n > length) {throw new IllegalArgumentException("length value "+n+" is illegal!");}// 求出該n所在bitMap的下標,等價于"n/5"int index = (int) n>>5;// 求出該值的偏移量(求余),等價于"n%31"int offset = (int) n & 31;/*** 等價于* int bits = bitsMap[index];* bitsMap[index]=bits| BIT_VALUE[offset];* 例如,n=3時,設置byte第4個位置為1 (從0開始計數,bitsMap[0]可代表的數為:0~31,從左到右每一個bit位表示一位數)* bitsMap[0]=00000000 00000000 00000000 00000000 | 00000000 00000000 00000000 00001000=00000000 00000000 00000000 00000000 00001000* 即: bitsMap[0]= 0 | 0x00000008 = 3** 例如,n=4時,設置byte第5個位置為1* bitsMap[0]=00000000 00000000 00000000 00001000 | 00000000 00000000 00000000 00010000=00000000 00000000 00000000 00000000 00011000* 即: bitsMap[0]=3 | 0x00000010 = 12*/bitsMap[index] |= BIT_VALUE[offset];}/*** 獲取值N是否存在* @return 1:存在,0:不存在*/public int isExist(long n) {if (n < 0 || n > length) {throw new IllegalArgumentException("length value illegal!");}int index = (int) n>>5;int offset = (int) n & 31;int bits = (int) bitsMap[index];// System.out.println("n="+n+",index="+index+",offset="+offset+",bits="+Integer.toBinaryString(bitsMap[index]));return ((bits & BIT_VALUE[offset])) >>> offset;}

BitMap應用

  1:看個小場景 > 在3億個整數中找出不重復的整數,限制內存不足以容納3億個整數。

  對于這種場景我可以采用2-BitMap來解決,即為每個整數分配2bit,用不同的0、1組合來標識特殊意思,如00表示此整數沒有出現過,01表示出現一次,11表示出現過多次,就可以找出重復的整數了,其需要的內存空間是正常BitMap的2倍,為:3億*2/8/1024/1024=71.5MB。

  具體的過程如下:

  掃描著3億個整數,組BitMap,先查看BitMap中的對應位置,如果00則變成01,是01則變成11,是11則保持不變,當將3億個整數掃描完之后也就是說整個BitMap已經組裝完畢。最后查看BitMap將對應位為11的整數輸出即可。

  2:已知某個文件內包含一些電話號碼,每個號碼為8位數字,統計不同號碼的個數。

  8位最多99 999 999,大概需要99m個bit,大概10幾m字節的內存即可。 (可以理解為從0-99 999 999的數字,每個數字對應一個Bit位,所以只需要99M個Bit==1.2MBytes,這樣,就用了小小的1.2M左右的內存表示了所有的8位數的電話)  

BitMap問題

  BitMap 的思想在面試的時候還是可以用來解決不少問題的,然后在很多系統中也都會用到,算是一種不錯的解決問題的思路。

  但是 BitMap 也有一些局限,因此會有其它一些基于 BitMap 的算法出現來解決這些問題。

  • 數據碰撞。比如將字符串映射到 BitMap 的時候會有碰撞的問題,那就可以考慮用 Bloom Filter 來解決,Bloom Filter?使用多個 Hash 函數來減少沖突的概率。
  • 數據稀疏。又比如要存入(10,8887983,93452134)這三個數據,我們需要建立一個 99999999 長度的 BitMap ,但是實際上只存了3個數據,這時候就有很大的空間浪費,碰到這種問題的話,可以通過引入 Roaring BitMap 來解決。

?另一種方式分析BitMap

 一、問題引入  

bitMap是位圖,其實準確的來說,翻譯成基于位的映射,舉一個例子,有一個無序有界int數組{1,2,5,7},初步估計占用內存44=16字節,這倒是沒什么奇怪的,但是假如有10億個這樣的數呢,10億*4字節/(1024*1024*1024)=3.72G左右(1GB=1024MB 、1MB=1024KB 、1KB=1024B 、1B=8b)。如果這樣的一個大的數據做查找和排序,那估計內存也崩潰了,有人說,這些數據可以不用一次性加載,那就是要存盤了,存盤必然消耗IO。我們提倡的是高性能,這個方案直接不考慮。 二、問題分析   如果用BitMap思想來解決的話,就好很多,解決方案如下:
  一個byte是占8個bit,如果每一個bit的值就是有或者沒有,也就是二進制的0或者1,如果用bit的位置代表數組值有還是沒有, 那么0代表該數值沒有出現過,1代表該數組值出現過。不也能描述數據了嗎?具體如下圖:

                  bitMap結構.p

  是不是很神奇,那么現在假如10億的數據所需的空間就是3.72G/32了吧,一個占用32bit的數據現在只占用了1bit,節省了不少的空間,排序就更不用說了,一切顯得那么順利。這樣的數據之間沒有關聯性,要是讀取的,你可以用多線程的方式去讀取。時間復雜度方面也是O(Max/n),其中Max為byte[]數組的大小,n為線程大小。

 三、應用與代碼
  如果BitMap僅僅是這個特點,我覺得還不是它的優雅的地方,接下來繼續欣賞它的魅力所在。下面的計算思想其實就是針對bit的邏輯運算得到,類似這種邏輯運算的應用場景可以用于權限計算之中。

  再看代碼之前,我們先搞清楚一個問題,一個數怎么快速定位它的索引號,也就是說搞清楚byte[index]的index是多少,position是哪一位。舉個例子吧,例如add(14)。14已經超出byte[0]的映射范圍,在byte[1]范圍之類。那么怎么快速定位它的索引呢。如果找到它的索引號,又怎么定位它的位置呢。Index(N)代表N的索引號,Position(N)代表N的所在的位置號。
  Index(N) = N/8 = N >> 3;
  Position(N) = N%8 = N & 0x07;

 (1) add(int num)
  你要向bitmap里add數據該怎么辦呢,不用擔心,很簡單,也很神奇。
  上面已經分析了,add的目的是為了將所在的位置從0變成1.其他位置不變.

add.png

代碼:

public void add(int num){// num/8得到byte[]的indexint arrayIndex = num >> 3; // num%8得到在byte[index]的位置int position = num & 0x07; //將1左移position后,那個位置自然就是1,然后和以前的數據做|,這樣,那個位置就替換成1了。bits[arrayIndex] |= 1 << position; }

 (2) clear(int num)

  對1進行左移,然后取反,最后與byte[index]作與操作。

  實例代碼:

public void clear(int num){// num/8得到byte[]的indexint arrayIndex = num >> 3; // num%8得到在byte[index]的位置int position = num & 0x07; //將1左移position后,那個位置自然就是1,然后對取反,再與當前值做&,即可清除當前的位置了.bits[arrayIndex] &= ~(1 << position); }

 (3) contain(int num)

?

?

public boolean contain(int num){ // num/8得到byte[]的indexint arrayIndex = num >> 3; // num%8得到在byte[index]的位置int position = num & 0x07; //將1左移position后,那個位置自然就是1,然后和以前的數據做&,判斷是否為0即可return (bits[arrayIndex] & (1 << position)) !=0; }

全部代碼:

public class BitMap {//保存數據的private byte[] bits;//能夠存儲多少數據private int capacity;public BitMap(int capacity){this.capacity = capacity;//1bit能存儲8個數據,那么capacity數據需要多少個bit呢,capacity/8+1,右移3位相當于除以8bits = new byte[(capacity >>3 )+1];}public void add(int num){// num/8得到byte[]的indexint arrayIndex = num >> 3; // num%8得到在byte[index]的位置int position = num & 0x07; //將1左移position后,那個位置自然就是1,然后和以前的數據做|,這樣,那個位置就替換成1了。bits[arrayIndex] |= 1 << position; }public boolean contain(int num){// num/8得到byte[]的indexint arrayIndex = num >> 3; // num%8得到在byte[index]的位置int position = num & 0x07; //將1左移position后,那個位置自然就是1,然后和以前的數據做&,判斷是否為0即可return (bits[arrayIndex] & (1 << position)) !=0; }public void clear(int num){// num/8得到byte[]的indexint arrayIndex = num >> 3; // num%8得到在byte[index]的位置int position = num & 0x07; //將1左移position后,那個位置自然就是1,然后對取反,再與當前值做&,即可清除當前的位置了.bits[arrayIndex] &= ~(1 << position); }public static void main(String[] args) {BitMap bitmap = new BitMap(100);bitmap.add(7);System.out.println("插入7成功");boolean isexsit = bitmap.contain(7);System.out.println("7是否存在:"+isexsit);bitmap.clear(7);isexsit = bitmap.contain(7);System.out.println("7是否存在:"+isexsit);} }

?

出處: https://my.oschina.net/freelili/blog/2885263

    http://www.cnblogs.com/wuhuangdi/p/4126752.html#3074215


轉載于:https://www.cnblogs.com/myseries/p/10880641.html

總結

以上是生活随笔為你收集整理的BitMap的原理和实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。