1. 程式人生 > >大資料處理之bitmap (java實現)

大資料處理之bitmap (java實現)

 問題提出:M(如10億)個int整數,只有其中N個數重複出現過,讀取到記憶體中並將重複的整數刪除。

    問題分析:我們肯定會先想到在計算機記憶體中開闢M個int整型資料陣列,來one bye one讀取M個int型別陣列, 然後在一一比對數值,最後將重複資料的去掉。當然這在處理小規模資料是可行的。

我們 考慮大資料的情況:例如在java語言下,對10億個int型別資料排重。

java中一個 int 型別在記憶體中佔4 byte。那麼10億個int型別資料共需要開闢10 ^ 9次方 *4 byte ≈ 4GB 的連續記憶體空間。以 32 位作業系統電腦為例,最大支援記憶體為 4G, 可用記憶體更是小於4G。所以上述方法在處理大資料時根本行不通。

    思維轉化:既然我們不能為所有 int 型別的資料開闢 int 型別陣列,那麼可以採取更小的資料型別來讀取快取 int 型別資料。考慮到計算機內部處理的資料都是 01 序列的bit,那麼我們是否可以用 1bit 來表示一個 int 型別資料。

  位對映的引出:使用較小的資料型別指代較大的資料型別。如上所說的問題,我們可以用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

 大小的 記憶體空間 。顯然,這樣處理就能滿足要求了。雖然對記憶體的消耗也不太小,暫時這樣處理吧。

問題解決方案: 首先定義如下圖的int - byte 對映關係,當然,對映關係可以自定義。但前提要保證你的陣列上下標不能越界。



 

但如上定義的bit[]陣列顯然在計算機中是不存在的,所我們需要將其轉化為 java 中的一個基本資料型別儲存。顯然,byte[] 是最好的選擇。

將其轉化為byte[] 陣列方案:

自定義的對映關係表,每個bit對應一個 int 數值,鄙人將 int 的最大值,最小值與陣列的最大最小索引相對應。從上圖可以看出來 int 數值與bit索引相差 2^31次方。當然,你也可以定義其他的對映關係,只是注意不要發生陣列越界的情況。由於最大值可能是2^32,故用long接收。

long bitIndex = num + (1l << 31);

計算在轉化為byte[]陣列的索引,由於上面定義的bitIndex 索引是非負數,故無需引入位運算去符號。

 int index = (int) (bitIndex / 8); 

計算bitIndex 在byte[]陣列索引index中的具體位置。

 int innerIndex = (int) (bitIndex % 8);

引入位運算將byte[]陣列索引index 的各個位按權值相加

dataBytes[index] = (byte) (dataBytes[index] | (1 << innerIndex));

這樣就解決了整個大資料讀取排重的問題。

那麼怎麼將其讀取出來呢?怎麼對資料進行排序?

那就只需要按照byte[]陣列進行一一對應到 int 型別資料上即可。

以下程式碼升序輸出為例。

遍歷陣列,採取與之前對映關係的逆運算來還原資料。

 for (int i = 0; i < bytes.length; i++) {

            for (int j = 0; j < 8; j++) {

                if (!(((bytes[i]) & (1 << j)) == 0)) {

                    int number = (int) ((((long) i * 8 + j) - (1l << 31)));

                }

            }

        } 

 }

由於編譯軟體預設設定的JVM記憶體是128—400M左右,測試此程式明顯是不夠的,所以需要調節一下分配給JVM的記憶體。否則,不管怎樣執行,都會出現Exception in thread "main" java.lang.OutOfMemoryError: Java heap space...

eclipse:選擇run->run configuration->arguments,輸入-Xms256M -Xmx1024M(-Xms代表jvm啟動時分配的記憶體大小,-Xmx代表可最大分配多少記憶體)

Intellij IDEA修改安裝目錄/IntelliJ IDEA 7.0/bin下idea.exe.vmoption檔案

    -Xms256M 
    -Xmx1024M 

原始碼:

Java程式碼  收藏程式碼
  1. package com.MassSort20131103;  
  2. import java.util.Random;  
  3. /** 
  4.  * Created with IntelliJ IDEA. 
  5.  * User: YangKang 
  6.  * Date: 13-11-3 
  7.  * Time:上午11:32 
  8.  * To change this template use File | Settings | File Templates. 
  9.  */  
  10. public class BigDataSort {  
  11.     private static final int CAPACITY = 1 000 000 000;//資料容量  
  12.     // 定義一個byte陣列快取所有的資料  
  13.     private byte[] dataBytes = new byte[1 << 29];  
  14.     public static void main(String[] args) {  
  15.         BigDataSort ms = new BigDataSort();  
  16.         byte[] bytes = null;  
  17.         Random random = new Random();  
  18.         for (int i = 0; i < CAPACITY; i++) {  
  19.             int num = random.nextInt();  
  20.             System.out.println("讀取了第 " + (i + 1) + "\t個數: " + num);  
  21.             bytes = ms.splitBigData(num);  
  22.         }  
  23.         System.out.println("");  
  24.         ms.output(bytes);  
  25.     }  
  26.     /** 
  27.      * 讀取資料,並將對應數資料的 到對應的bit中,並返回byte陣列 
  28.      * @param num 讀取的資料 
  29.      * @return byte陣列  dataBytes 
  30.      */  
  31.     private byte[] splitBigData(int num) {  
  32.         long bitIndex = num + (1l << 31);         //獲取num資料對應bit陣列(虛擬)的索引  
  33.         int index = (int) (bitIndex / 8);         //bit陣列(虛擬)在byte陣列中的索引  
  34.         int innerIndex = (int) (bitIndex % 8);    //bitIndex 在byte[]陣列索引index 中的具體位置  
  35.         System.out.println("byte[" + index + "] 中的索引:" + innerIndex);  
  36.         dataBytes[index] = (byte) (dataBytes[index] | (1 << innerIndex));  
  37.         return dataBytes;  
  38.     }  
  39.     /** 
  40.      * 輸出陣列中的資料 
  41.      * @param bytes byte陣列 
  42.      */  
  43.     private void output(byte[] bytes) {  
  44.         int count = 0;  
  45.         for (int i = 0; i < bytes.length; i++) {  
  46.             for (int j = 0; j < 8; j++) {  
  47.                 if (!(((bytes[i]) & (1 << j)) == 0)) {  
  48.                     count++;  
  49.                     int number = (int) ((((long) i * 8 + j) - (1l << 31)));  
  50.                     System.out.println("取出的第  " + count + "\t個數: " +  number);  
  51.                 }  
  52.             }  
  53.         }  
  54.     }  
  55. }  

原文連結:http://yacare.iteye.com/blog/1969931