JAVA数据结构-位图延伸RoaringBitmap

4,808 阅读2分钟

RoaringBitMap是什么?

RoaringBitMapBitSet的延伸,其在BitSet的基础上对底层结构进行了压缩,减小了内存空间的占用。与未压缩的传统BitSet相比,其性能可能优于其上百倍并且速度会更快。

有什么用?

位图这种数据源结构会用在人物画像方面,某个用户标签下有什么用户,其位上的值则表示在该标签下的用户标识。另外RoaringBitmap支持andandNotorxor集合操作,可以实现不同标签的逻辑操作,比如说女生标签L1、年龄>=25两个标签L2,先要求找出年龄大于25的女生,则可以很方面的操作L1.and(L2)。

怎么用?

基本用法

RoaringBitMap基本用法比较简单

 public static void main(String[] args) {
        RoaringBitmap rr = RoaringBitmap.bitmapOf(1,2,3,1000);	//带初始化操作的实例
        RoaringBitmap rr2 = new RoaringBitmap();
        rr2.add(4000L,4255L);//范围添加
        rr.select(3); // 还回bitmap中index为3的值,这里是1000
        rr.rank(2); // 还回2在bitmap中的index,这是1
        rr.contains(1000); // 1000在其中
        rr.contains(7); // 7不在其中

        RoaringBitmap rror = RoaringBitmap.or(rr, rr2);// 逻辑or操作
        rr.or(rr2); //逻辑or操作,rr=rr.or(rr2);
        boolean equals = rror.equals(rr);// true
        if(!equals) throw new RuntimeException("bug");
        long cardinality = rr.getLongCardinality();//bitmap中value的个数
        System.out.println(cardinality);

序列化

标签记载都是很费时间的,因为要去聚合数据和计算,所以当程序不得不停止时,我们如何把这些数据从内存保存到磁盘,然后程序启动时将这些数据从磁盘加载到内存呢,这里就要用到序列化的知识。

Stream序列化

序列化

    //序列化路径
    private static String  path = "E:\\data\\out.txt";

    public static void main(String[] args)throws Exception {
        RoaringBitmap rr = RoaringBitmap.bitmapOf(9,3,4,6,7);
        rr.add(158);
        System.out.println(rr.select(3));
        System.out.println(rr.rank(3));
        System.out.println(rr.contains(6));
        System.out.println(rr.getLongCardinality());
        //序列化
        FileOutputStream fos = new FileOutputStream(path);
        DataOutputStream dos = new DataOutputStream(fos);
        rr.serialize(dos);
        //数据刷新到磁盘
        dos.flush();
        dos.close();
        }

反序列化

    ////二进制文件路径
    private static String  path = "E:\\data\\out.txt";

    public static void main(String[] args)throws Exception {
        //读取二进制流并包装成ByteBuf
        FileInputStream fi = new FileInputStream(path);
        DataInputStream di = new DataInputStream(fi);
        byte[] bytes = new byte[di.available()];
        di.read(bytes,0,di.available());
        ByteBuffer bb = ByteBuffer.wrap(bytes);
        //ByteBuf还原数据结构
        ImmutableRoaringBitmap immutableRoaringBitmap = new ImmutableRoaringBitmap(bb);
        RoaringBitmap rr = new RoaringBitmap(immutableRoaringBitmap);
        System.out.println(rr.select(3));
        System.out.println(rr.rank(3));
        System.out.println(rr.contains(6));
    }

原理

RoaringBitMap使用的以下位图索引压缩算法: 1.我们将 32-bit 的范围 ([0, n)) 划分为 2^16 个桶,每一个桶有一个 Container 来存放一个数值的低16位;
2.在存储和查询数值的时候,我们将一个数值 k 划分为高 16 位(k % 2^16)和低 16 位(k mod 2^16),取高 16 位找到对应的桶,然后在低 16 位存放在相应的 Container 中;
3.容器的话, RBM 使用两种容器结构: Array Container 和 Bitmap Container。Array Container 存放稀疏的数据,Bitmap Container 存放稠密的数据。即,若一个 Container 里面的 Integer 数量小于 4096,就用 Short 类型的有序数组来存储值。若大于 4096,就用 Bitmap 来存储值。