位图实现(Java版本)


是否因为hbase中的布尔过滤器的实现而感到疑惑呢?其实布尔过滤器这种存储结构的是基于多位图的,其内部存储的是多个位图结构。本篇我们了解一下位图这种数据结构。

本质

位图其本质就是利用hash函数映射的一种map数据结构,我们知道java中的hashmap数据结构其底层仍然是以hash函数映射的数字作为key,其value是以链表or数组的方式存储数据的,一旦使用了hash函数,就意味着存在hash碰撞的可能性。
位图的存在,其本质就是对hashMap的一种更加节约内存的优化,其目的就是检测一个值是否存在或不存在,在一些应用场景,比如爬虫结构中的url去重功能,缓存实现是否已经被加载过等等,也就是说在存在性命题的应用场景中会经常碰到位图数据结构的应用,

位图的特点

 1. 查询高效(O(1)复杂度)
 2. 节约内存(1亿不同的key,总共需要12.5m)

我们知道,使用hashMap的key值,我们是可以约定数据类型的比如,integer/long/string等等数据结构作为key的,因此在某种意义上来讲,key所存储的空间在一开始就被定死了,那能否充分利用key的大小,最大程度

位图示意图

在这里插入图片描述
通过上图,主要想表达,位图,只有一个大数组(以数组形式构造的map结构),并且利用数组在高速缓存的连续性特点,能够高效地读取做操作,在位图中的key是通过hash函数映射成一个整形值,并根据该整形值查找相应数组的位置,并设置相应数据值中的位数置为1即可。

位图映射规则

位图映射的规则就是 为一长串的二进制数的对应位标记为1就行了,其优点就是不用像存于hashmap的key那样耗内存
比如我们存储的是数组中的第一二个字节:

1000000000001000 表示在位图中存储的是16 与 4 这两个数

如何判断位图中是否存在某数

依据上面的例子 当我们判断位图中是否存在4的时候,只要取出这个数字对应在位图中的字符元素 并与 4本身的二进制位进行 合并(&)运算,只要其计算结果等于4本身的二进制就可以了

代码实现

github地址

import com.carrotsearch.sizeof.RamUsageEstimator;
class MyBitSet{
        private final char[] aChar; // 我们定义一个字符数组其大小
        private final int size;
        public MyBitSet(int size){
        	// 初始化一个可以存size大小的位图
        	// 这里当size为16时候,由于每个char元素占用16位,所以,我们初始化
        	// 1个字节的字节数组就可以了
            aChar = new char[ size % 16 == 0? size / 16  : size /16 +1];
            this.size = size;
        }
	 	///存储i数字
        public void set(int i) {
			// 当我们i能被16整除的时候
            int index = i % 16 == 0? i / 16 - 1 : i /16;
            int splitindex = 1 << (i - index * 16 - 1);
            aChar[index] = (char) (aChar[index] | splitindex);
        }
        public boolean get(int i) {
            int index = i % 16 == 0? i / 16 -1 : i /16;
            int splitindex = 1 << (i - index * 16 - 1);
            int i1 = aChar[index] & splitindex;
            return i1 == splitindex;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < aChar.length; i++) {
                sb.append(aChar[i]);
                System.out.println(Integer.toBinaryString(aChar[i]));
            }
            return sb.toString();
        }
        public static void main(String[] args) {

        int size = 1234567890; // 存储12亿3千万个数组
        MyBitSet myBitSet = new MyBitSet(size);
        myBitSet.set(1);
        myBitSet.set(2);
        myBitSet.set(3);
        myBitSet.set(31);
        myBitSet.set(32);
        myBitSet.set(16);

        for (int i = 1; i <= size; i++) {
            boolean b = myBitSet.get(i);
            if(b) System.out.println(i);
        }
        long l = RamUsageEstimator.sizeOf(myBitSet);
        System.out.println("size: "+l); //  size: 154321032 140m数据
    }
    }

我们使用com.carrotsearch.java-sizeof:0.05 包中的计算对象大小的函数,可以获得在位图中存储12亿的数字,只需要140m左右的内存空间,
如果使用hashmap会有多大呢?据估计要400m左右
因此位图存储数值的内存空间比hashmap更节约内存,而且在O(1)的时间复杂度内就能定位到元素,比hashmap会有一定的性能提升,因为hashmap在查找值的时候不仅要定位到hashmap指定key上,还要对key所链接的元素进行逐一比对。

java实现类

其实java也有内置的位图已经实现好了

package java.util.BitSet

发布了41 篇原创文章 · 获赞 14 · 访问量 6万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章