BitMap数据结构梳理总结及代码实现

BitMap(位图)

BitMap定义

位图(BitMap),即位(Bit)的集合,是一个离散的数组结构,用一个bit位来标记某个元素对应的Value,而Key即是该元素;最基本的情况,使用一个bit表示一个关键字的状态(可标示两种状态0-不存在,1-存在),也可以使用2bit(表示4种状态),3bit(表示8种状态)需要根据业务场景实现。

BitMap 数据结构

数据结构:byte[],一个byte 8 bit,使用bit为单位来存储数据,可以在空间和时间双重维度提高效率。

注:也可以是 int[],long[],数据结构,分别表示32bit,64bit

一个int 4个字节 32 bit,使用 BitMap(byte[]) 一个bit 表示一个int 空间缩小32倍。

如:在Java里面一个int类型占4个字节,要对于20亿个int数据进行处理,20亿*4/1024/1024/1024≈7.5G左右,需要将尽8G的内存;BitMap(byte[])处理需要20亿/8/1024/1024/1024≈0.232G,内存不到0.5G,查询某个数据时间复杂度是o(n),校验数据时间复杂度o(1)

数组:[7,9,11,1,3,5],存储如下图

BitMap数据结构梳理总结及代码实现

注:byte[] 下标  n/8 == n >>> 3 即向右位移3位;byte[]值域 1<< n%8 = ( n & 0x07) 即取模向左位移

BitMap 应用场景

1.多个超大文件取交集(数字文件)

2.从海量电话号码中,统计不同号码的个数

3.统计日活,访问量,如 redis.setbit('YYYY-MM-DD',Id,1);

4.BitMap处理大量数字型数据的排序、查询、去重

5.BitMap扩展——Bloom Filter(布隆过滤器)过滤数据(id)

BitMap 优缺点

优点:

1.运算效率高,不需要进行比较和移位;

2.占用内存少,比如: 20亿/8/1024/1024/1024≈0.232G

3.高效排序,去重,校验o(1),查询o(n)

4.可删除(存储数值型数据)

缺点:

1.所有的数据不能重复。即不可对重复的数据进行排序和查找。

2.只有当数据比较密集时才有优势

3.数据碰撞。比如将字符串hash映射到 BitMap 的时候会有碰撞的问题,那就可以考虑用 Bloom Filter 来解决,Bloom Filter 使用多个 Hash 函数来减少冲突的概率。

4.数据稀疏。比如要存入(9,55555555,99999999)这三个数据,需要建立一个 99999999/8+1 长度的 BitMap ,但是实际上只存了3个数据,这时候就会很大的空间浪费(少量数据不建议使用);可以通过引入 Roaring BitMap 来解决。

BitMap 手动实现源码逻辑

本次底层是数据结构使用byte[],实现,也可以使用 int[],long[]数据结果,注意初始化数组大小,防止内存不够

/**

 * java运算符 与(&)、非(~)、或(|)、异或(^)

 * BitMap 数据存储约 160 亿

 * 存储方式为离散数组方式,建议大量连续数据使用

 * @author ysf

 *

 */

public class BitMap {

      private static final int MAXSIZE = Integer.MAX_VALUE-Short.MAX_VALUE;

      private byte[] bits;

      private long count=0;

      public BitMap() {

            bits = new byte[Integer.MAX_VALUE/2];

      }

      public BitMap(long maxNum) {

            long maxSize = (maxNum>>>3)+1;

            if(maxSize > Long.valueOf(MAXSIZE)){

                  bits = new byte[MAXSIZE];

            }else{

                  bits = new byte[(int)maxSize];

            }

      }

      public BitMap(int count) {

            bits = new byte[count];

      }

      /**

       * 添加数据元素,每一bit标识一个数据(0-不存在,1-存在)

       * @param i

       */

      public void add(long i){

            // 取模

            int r = (int)(i>>3);

            // 取余

            int c = (int)(i & 0x07);

            // | 或运算 只要一个为1,即为1

            if(contains(i)){

                  return;

            }

            bits[r] |=  (1 << c);

            count++;

      }

      /**

       * 验证数据是否存在

       * @param i

       * @return

       */

      public boolean contains(long i){

            // 取模

            int r = (int) (i>>3);

            // 取余

            int c = (int)(i & 0x07);

            // & 与运算 两个都为1,即为1

            if (((bits[r] >>> c) & 1) == 1) {

                  return true;

            }

           return false;

      }

      /**

       * 移除数据

       * @param i

       */

      public void remove(long i){

            // 取模

            int r = (int) (i>>3);

            // 取余

            int c = (int)(i & 0x07);

            if(!contains(i)){

                  return;

            }

            bits[r] &= ~(1 << c);

            count--;

      }

      /**

       * 获取数据记录总数 bit

       * @return

       */

      public long getCount(){

            return count;

      }

      /**

             * 获取下标元素数据,因为离散型数组存储,需要遍历全部数据

             * @param subscript

             * @return long

       */

      public long getNum(long subscript){

            long size = (long)bits.length<<3;

            long sub = 0;

            for(long i= 0;i < size;i++){

                 if(contains(i)){

                        if(sub == subscript){

                             return i;

                       }

                       sub++;

                 }

            }

            return 0;

      }

}

BitMap Bloom Filter扩展

注:布隆过滤器是以BitMap基本原理实现的扩展

布隆过滤器是一个含有 m 个元素的位数组(元素为0或1),每一位初始为0;并含有 k 个独立的哈希函数 h1, h2,..., hk 。

需要将集合中的元素加入到布隆过滤器中,依次计算h1(x), h2(x),...,hk(x),其计算结果填充对应数组的位置,并将其全部置1。一个位置可以被多次置1,但只有一次有效。

当查询某个元素是否在集合中时,计算这 k 个哈希函数,只有当其计算结果全部为1时,则认为可能存在;只要有1个计算结果为0时,则必不存在。

注:布隆过滤器存在假阳性的可能,即当所有哈希值都为1时,该元素也可能不在集合内,但该算法认为在里面。假阳性出现的概率被哈希函数的数量、位数组大小、以及集合元素等因素决定

BitMap Bloom Filter 优缺点与BitMap大致一样

  • 优点:
    • 空间效率高,所占空间小。
    • 查询时间短。
    • 自带去重
  • 缺点:
    • 元素添加到集合中后,不能被删除。
    • 有一定的误判率

布谷鸟过滤器与布隆过滤器比较

  1. 支持动态删除项;
  2. 更好的查找性能;
  3. 对于需要低假阳性率的应用程序(ϵ<3%)有更好的空间利用率

BitMap Bloom Filter 使用场景

  1. 大量数据中判断某个数据是否存在
  2. 解决缓存穿透
  3. 爬虫url/ 邮箱等系统的过滤

注:Bloom Filtet 实现请参考google,hutool

<dependency>

       <groupId>com.google</groupId>

      <artifactId>guava</artifactId>

      <version>28.0-jre</version>

</dependency>

<dependency>

       <groupId>cn.hutool</groupId>

      <artifactId>hutool-all</artifactId>

      <version>4.1.12</version>

</dependency>

上一篇:Arcgis api for javascript学习笔记(4.5版本) - 点击多边形(Polygon)并高亮显示


下一篇:数据类型进行运算的问题以及常量优化机制