大資料處理之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程式碼- package com.MassSort20131103;
- import java.util.Random;
- /**
- * Created with IntelliJ IDEA.
- * User: YangKang
- * Date: 13-11-3
- * Time:上午11:32
- * To change this template use File | Settings | File Templates.
- */
- public class BigDataSort {
- private static final int CAPACITY = 1 000 000 000;//資料容量
- // 定義一個byte陣列快取所有的資料
- private byte[] dataBytes = new byte[1 << 29];
- public static void main(String[] args) {
- BigDataSort ms = new BigDataSort();
- byte[] bytes = null;
- Random random = new Random();
- for (int i = 0; i < CAPACITY; i++) {
- int num = random.nextInt();
- System.out.println("讀取了第 " + (i + 1) + "\t個數: " + num);
- bytes = ms.splitBigData(num);
- }
- System.out.println("");
- ms.output(bytes);
- }
- /**
- * 讀取資料,並將對應數資料的 到對應的bit中,並返回byte陣列
- * @param num 讀取的資料
- * @return byte陣列 dataBytes
- */
- private byte[] splitBigData(int num) {
- long bitIndex = num + (1l << 31); //獲取num資料對應bit陣列(虛擬)的索引
- int index = (int) (bitIndex / 8); //bit陣列(虛擬)在byte陣列中的索引
- int innerIndex = (int) (bitIndex % 8); //bitIndex 在byte[]陣列索引index 中的具體位置
- System.out.println("byte[" + index + "] 中的索引:" + innerIndex);
- dataBytes[index] = (byte) (dataBytes[index] | (1 << innerIndex));
- return dataBytes;
- }
- /**
- * 輸出陣列中的資料
- * @param bytes byte陣列
- */
- private void output(byte[] bytes) {
- int count = 0;
- for (int i = 0; i < bytes.length; i++) {
- for (int j = 0; j < 8; j++) {
- if (!(((bytes[i]) & (1 << j)) == 0)) {
- count++;
- int number = (int) ((((long) i * 8 + j) - (1l << 31)));
- System.out.println("取出的第 " + count + "\t個數: " + number);
- }
- }
- }
- }
- }
原文連結:http://yacare.iteye.com/blog/1969931