利用BitMap進行大數據排序去重

一、問題

問題提出java

M(如10億)個int整數,只有其中N個數重複出現過,讀取到內存中並將重複的整數刪除。數組

二、解決方案

問題分析緩存

咱們確定會先想到在計算機內存中開闢M個int整型數據數組,來one bye one讀取M個int類型數組, 而後在一一比對數值,最後將重複數據的去掉。固然這在處理小規模數據是可行的。dom

咱們考慮大數據的狀況:例如在java語言下,對10億個int類型數據排重。ide

java中一個int類型在內存中佔4byte。那麼10億個int類型數據共須要開闢10^9次方*4byte≈4GB的連續內存空間。以32位操做系統電腦爲例,最大支持內存爲4G,可用內存更是小於4G。因此上述方法在處理大數據時根本行不通。大數據

思惟轉化spa

既然咱們不能爲全部 int 類型的數據開闢 int 類型數組,那麼能夠採起更小的數據類型來讀取緩存 int 類型數據。考慮到計算機內部處理的數據都是 01 序列的bit,那麼咱們是否能夠用 1bit 來表示一個 int 類型數據。操作系統

位映射的引出code

使用較小的數據類型指代較大的數據類型。如上所說的問題,咱們能夠用1個 bit 來對應一個int 整數。假如對應的 int 類型的數據存在,就將其對應的 bit 賦值爲1,不然,賦值爲0(boolean類型)。java中 int 範圍爲 -2^31 到 2^31-1. 那麼全部可能的數值組成的長度爲2^32. 對應的 bit 長度也爲 2^32. 那麼能夠用這樣處理以後只須要開闢2^32 bit = 2^29 byte = 512M 大小的 內存空間 。顯然,這樣處理就能知足要求了,雖然對內存的消耗也不過小。blog

問題解決方案

首先定義以下圖的int - byte 映射關係,固然,映射關係能夠自定義。但前提要保證你的數組上下標不能越界。

但如上定義的bit[]數組顯然在計算機中是不存在的,所咱們須要將其轉化爲 java 中的一個基本數據類型存儲。顯然,byte[] 是最好的選擇。

將其轉化爲byte[] 數組方案:

自定義的映射關係表,每一個bit對應一個 int 數值,將 int 的最大值,最小值與數組的最大最小索引相對應。從上圖能夠看出來 int 數值與bit索引相差 2^31次方。固然,你也能夠定義其餘的映射關係,只是注意不要發生數組越界的狀況。

bit[]索引:因爲最大值多是2^32,故用long接收: long bitIndex = num + (1l << 31); 

byte[]索引:  int index = (int) (bitIndex / 8);  ,在字節byte[index]中的具體位置:  int innerIndex = (int) (bitIndex % 8); 

更新值: dataBytes[index] = (byte) (dataBytes[index] | (1 << innerIndex)); 

 

三、代碼

 1 import java.util.Random;
 2 
 3 /**
 4  * 問題:M(如10億)個int整數,只有其中N個數重複出現過,讀取到內存中並將重複的整數刪除。<br/>
 5  * 使用位映射來進行海量數據的去重排序,原先一個元素用一個int如今只用一個bit, 內存佔比4*8bit:1bit=32:1<br/>
 6  * 亦可用java語言提供的BitSet,不過其指定bit index的參數爲int類型,所以在此例中將輸入數轉爲bit index時對於較大的數會越界<br><br/>
 7  */
 8 public class BigDataSort {
 9 
10     private static final int CAPACITY = 1_000_000;// 數據容量
11 
12     public static void main(String[] args) {
13 
14         testMyFullBitMap();
15 
16     }
17 
18     public static void testMyFullBitMap() {
19         MyFullBitMap ms = new MyFullBitMap();
20 
21         byte[] bytes = null;
22 
23         Random random = new Random();
24         long startTime = System.currentTimeMillis();
25         for (int i = 0; i < CAPACITY; i++) {
26             int num = random.nextInt();
27             // System.out.println("讀取了第 " + (i + 1) + "\t個數: " + num);
28             bytes = ms.setBit(num);
29         }
30         long endTime = System.currentTimeMillis();
31         System.out.printf("存入%d個數,用時%dms\n", CAPACITY, endTime - startTime);
32 
33         startTime = System.currentTimeMillis();
34         ms.output(bytes);
35         endTime = System.currentTimeMillis();
36         System.out.printf("取出%d個數,用時%dms\n", CAPACITY, endTime - startTime);
37     }
38 }
39 
40 class MyFullBitMap {
41     // 定義一個byte數組表示全部的int數據,一bit對應一個,共2^32b=2^29B=512MB
42     private byte[] dataBytes = new byte[1 << 29];
43 
44     /**
45      * 讀取數據,並將對應數數據的 到對應的bit中,並返回byte數組
46      * 
47      * @param num
48      *            讀取的數據
49      * @return byte數組 dataBytes
50      */
51     public byte[] setBit(int num) {
52 
53         long bitIndex = num + (1l << 31); // 獲取num數據對應bit數組(虛擬)的索引
54         int index = (int) (bitIndex / 8); // bit數組(虛擬)在byte數組中的索引
55         int innerIndex = (int) (bitIndex % 8); // bitIndex 在byte[]數組索引index 中的具體位置
56 
57         // System.out.println("byte[" + index + "] 中的索引:" + innerIndex);
58 
59         dataBytes[index] = (byte) (dataBytes[index] | (1 << innerIndex));
60         return dataBytes;
61     }
62 
63     /**
64      * 輸出數組中的數據
65      * 
66      * @param bytes
67      *            byte數組
68      */
69     public void output(byte[] bytes) {
70         int count = 0;
71         for (int i = 0; i < bytes.length; i++) {
72             for (int j = 0; j < 8; j++) {
73                 if (((bytes[i]) & (1 << j)) != 0) {
74                     count++;
75                     int number = (int) ((((long) i * 8 + j) - (1l << 31)));
76                     // System.out.println("取出的第 " + count + "\t個數: " + number);
77                 }
78             }
79         }
80     }
81 }
View Code

四、參考資料

http://yacare.iteye.com/blog/1969931

相關文章
相關標籤/搜索