海量数据下的去重和查重(一):BitMap位图法

在一些海量数据的场景中,做一些查重、去重、排序,一般的方法难以实现,因为内存占用太大了,比如以下问题:

问题一:10亿个正整数,给定一个数值,如何快速判定该数值是否在10亿个正整数当中?假如机器只有1G内存?

问题二:比如说是一组数字,它是保存在一个很大的文件中。它总体的个数为400亿个,里面有大量重复的数据,如果去除重复的元素之后,大概的数据有40个亿,那么,假定我们有一台内存为2GB的机器。我们该如何来消除其中重复的元素呢?再进一步考虑,如果我们消除了重复的元素之后,怎么统计里面元素的个数并将去重后的元素保存到另外的一个结果文件里呢?

我们来估计一下上面所需要用到的内存,1G 约等于 10^9(10的9次方)个字节,上面的数字都是int型,一个int 占用4字节,所以问题一可能需要占用4G内存,而问题二中,需要占用16G内存,显然需要的内存太大了,传统的方法是不能用了,这时候就需要引出今天的主角了——BitMap位图法。

1、思想

位图法的思想类似于hash寻址,首先初始化一个int数组,每个元素对应32位比特(一个int 占用4字节,1字节8个比特位),将10亿个元素分别读入内存,对int数组中的元素比特位进行标记,如果存在,标记为1即可。标记完之后,即可快速判定某个元素是否在10亿个正整数当中,时间复杂度为O(1)。

image.png

原先一个int类型只能表示一个整数,现在一个int能表示32个整数,相当于是节省了32倍的内存。

那么具体我们是怎么操作??

  • 寻址
    比如元素 119,如何确定其对应的数组比特位 index ?
    1)确定数组 arrayIndex :119/32 = 3,也就是第 4 个数组元素,a[3] 的位置。——num >> 5(num右移5位,相当于是除以2的5次方);
    2)确定比特位 bitIndex:119%32 = 23,第23个比特位。—— num & 31
  • 设置比特位
    • 将比特位设置为1
      bigArray[arrayIndex] |= 1 << bitIndex(1左移到目标位置,然后进行或运算);
    • 将比特位设置为0
      bigArray[arrayIndex] &= ~(1 << bitIndex);(1左移到目标位置,然后取反,然后再进行与运算);
  • 判断某一元素是否存在
    只需将 1 左移bitIndex位数,然后与原来的值进行与运算。只要与运算结果中有1,即表示元素存在。所以可以用运行结果是不为0作为元素是否存在依据。

2、实现

2.1 自定义实现
import java.util.BitSet;

public class bitMap {
        private int[] bigArray;

        public bitMap(long  size){
            bigArray = new int[(int) (size/ 32 + 1)];
        }

        public void set1(int  num){
            //确定数组 index
            int arrayIndex = num >> 5;
            //确定bit index
            int bitIndex = num & 31;
            //设置0
            bigArray[arrayIndex] |= 1 << bitIndex;
        }

        public void set0(int  num){
            //确定数组 index
            int arrayIndex = num >> 5;
            //确定bit index
            int bitIndex = num & 31;
            //设置0
            bigArray[arrayIndex] &= ~(1 << bitIndex);
            System.out.println(get32BitBinString(bigArray[arrayIndex]));
        }

        public boolean isExist(int  num){
            //确定数组 index
            int arrayIndex = num >> 5;
            //确定bit index
            int bitIndex = num & 31;

            //判断是否存在
            return (bigArray[arrayIndex] & (1 << bitIndex))!=0 ? true : false;
        }

        /**
         * 将整型数字转换为二进制字符串,一共32位,不舍弃前面的0
         * @param number 整型数字
         * @return 二进制字符串
         */




    private static String get32BitBinString(int number) {
        StringBuilder sBuilder = new StringBuilder();
        for (int i = 0; i < 32; i++){
            sBuilder.append(number & 1);
            number = number >>> 1;
        }
        return sBuilder.reverse().toString();
    }

    public static void main(String[] args) {

        int[] arrays = new int[]{1, 2, 35, 22, 56, 334, 245, 2234, 54};

        bitMap bigMapTest = new bitMap(2234-1);

        for (int i : arrays) {
            bigMapTest.set1(i);
        }
        System.out.println(bigMapTest.isExist(36));
    }

}
2.1 jdk实现

在java.util包中有个BitSet类也是用同样的思想实现的,不过BitSet的底层实现是long数组(long是64位,占用8个字节)。

public class BitSetTest {

    public static void main(String[] args) {
        int [] array = new int [] {1,2,3,22,0,3,63};
        BitSet bitSet  = new BitSet(1);
        System.out.println(bitSet.size());   //64
        bitSet  = new BitSet(65);
        System.out.println(bitSet.size());   //128
        bitSet  = new BitSet(23);
        System.out.println(bitSet.size());   //64

        //将数组内容组bitmap
        for(int i=0;i<array.length;i++)
        {
            bitSet.set(array[i], true);
        }

        System.out.println(bitSet.get(22));
        System.out.println(bitSet.get(60));

        System.out.println("下面开始遍历BitSet:");
        for ( int i = 0; i < bitSet.size(); i++ ){
            System.out.println(bitSet.get(i));
        }
    }

}

3、优缺点

  • 优点:是海量数据下去重时,用这种数据结构可以大量的节省空间;
  • 缺点:
      1. 空间占用大小是根据最大值来的,如果数据量本身不多,但是最大值特别大,那不但不会节省空间,反而会浪费空间
      1. 功能有限,由于bit位只能是0和1,所以只能用于去重和查重,如果要统计每个元素出现的个数,就不能实现了。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249

推荐阅读更多精彩内容