架构师JAVA

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

2020-01-06  本文已影响0人  雪飘千里

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

问题一: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倍的内存。

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

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、优缺点

上一篇下一篇

猜你喜欢

热点阅读