BitMap原理与实现

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BitMap原理与实现相关的知识,希望对你有一定的参考价值。

参考技术A

比较经典的问题是: 在只能够使用2G的内存中,如何完成以下操作:
①:对10亿个不重复的整数进行排序。
②:找出10亿个数字中重复的数字。
无论是排序还是找重复的数字都需要将这10亿个数字加入到内存中在去进行操作,很明显,题目给出的2G内存限制说明了在这样的场景下是不能够将所有数都加入到内存中的
1000000000* 4/(1024* 1024* 1024) = 3.725G
那么这时候就需要用到 BitMap结构了

bitMap使用一个bit为0/1作为map的value来标记一个数字是否存在,而map的key值正是这个数字本身。
相比于一般的数据结构需要用4个byte去存储数值本身,相当于是节省了 4*8:1 = 32倍的内存空间

bitMap不一定要用bit数组,可以使用 int,long等等的基本数据类型实现,因为其实质都是在bit位上存数据,用哪种类型只是决定了最终实现出来的BitMap的内置数组中单个元素存放数据的多少
    例如:java中的BitSet使用Long数组
BitMap的实现当然少不了位运算,先来明确几个常见位运算,这是实现BitMap的基础:

set(bitIndex): 添加操作
    1 .确定该数处于数组中的哪个元素的位上
     int wordIndex = bitIndex >> 5;
因为我用的是int[]实现,所以这里右移 5 位(2^5 = 32)
    2 .确定相对于该元素中的位置偏移
     int bitPosition = bitIndex & ((1 << 5) - 1);
这里相当于是 bitIndex % (1<<5)的取模运算,因为当取模运算的除数是2的次幂,所以可以使用以下的位运算来计算,提升效率(对比HashMap的容量为什么总是2的幂次方的问题,HashMap求下标时也是使用 hash&(n-1))
tips: 位运算的优先级是低于+,-等等的,所以要加上括号,防止发生不可描述的错误
    3 .将该位置1
     bits[wordIndex] |= 1 << bitPosition;
相当于是将指定位置处的bit值置1,其他位置保持不变,也就是将以这个bitIndex为key的位置为1
tips: 这里是参考了网上的各位大佬的文章,取余 + 按位或,又对比了下BitSet的源码:
     words[wordIndex] |= (1L << bitIndex);
没有取余操作,直接|,这两个一样吗?答案当然是一样的
举个栗子:
     1 << 148 == 1<< 20     
     1L << 125 ==1L<< 61
即对于int和long型数据,直接左移其位数相当于是附带了对其的取模操作

总结 :使用Bit-map的思想,我们可以将存储空间进行压缩,而且可以对数字进行快速排序、去重和查询的操作。
Bloom Fliter是Bit-map思想的一种扩展,它可以在允许低错误率的场景下,大大地进行空间压缩,是一种拿错误率换取空间的数据结构

当一个元素加入布隆过滤器中的时候,会进行哪些操作:

当我们需要判断一个元素是否存在于布隆过滤器的时候,会进行哪些操作:

然后,一定会出现这样一种情况: 不同的字符串可能哈希出来的位置相同 (可以适当增加位数组大小或者调整我们的哈希函数来降低概率),因此: 布隆过滤器可能会存在误判的情况

总结来说就是: 布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在

Bloom Filter的应用: 常用于解决缓存穿透等场景。

图像与Bitmap类

我无法理解Image类和Bitmap类之间的差异。现在,我知道Bitmap继承自Image,但据我所知,两者非常相似。有人可以对此有所了解吗?

答案

Bitmap类是Image类的实现。 Image类是一个抽象类;

Bitmap类包含12个构造函数,这些构造函数从不同的参数构造Bitmap对象。它可以从另一个位图构造Bitmap,以及图像的字符串地址。

查看更多in this comprehensive sample

另一答案

这是一个澄清,因为我已经看到在代码中完成的事情真的令人困惑 - 我认为以下示例可能会帮助其他人。

正如其他人之前所说的那样 - Bitmap继承自Abstract Image类

Abstract有效意味着您无法创建它的New()实例。

    Image imgBad1 = new Image();        // Bad - won't compile
    Image imgBad2 = new Image(200,200); // Bad - won't compile

但是你可以做到以下几点:

    Image imgGood;  // Not instantiated object!
    // Now you can do this
    imgGood = new Bitmap(200, 200);

您现在可以像使用相同的位图对象一样使用imgGood,如果您已完成以下操作:

    Bitmap bmpGood = new Bitmap(200,200);

这里的好处是你可以使用Graphics对象绘制imgGood对象

    Graphics gr = default(Graphics);
    gr = Graphics.FromImage(new Bitmap(1000, 1000));
    Rectangle rect = new Rectangle(50, 50, imgGood.Width, imgGood.Height); // where to draw
    gr.DrawImage(imgGood, rect);

这里的imgGood可以是任何Image对象 - Bitmap,Metafile或其他任何继承自Image的东西!

另一答案

Image提供对任意图像的抽象访问,它定义了一组可以在Image的任何实现上进行逻辑应用的方法。它不受任何特定图像格式或实现的限制。位图是图像抽象类的特定实现,它封装了窗口GDI位图对象。 Bitmap只是Image抽象类的一个特定实现,它在GDI位图Object上进行中继。

例如,您可以通过继承Image类并实现抽象方法,为Image摘要创建自己的实现。

无论如何,这只是OOP的一个简单的基本用法,它应该不难捕捉。

以上是关于BitMap原理与实现的主要内容,如果未能解决你的问题,请参考以下文章

货拉拉大数据对Bitmap的探索与实践(下)

BitMap的原理和实现

qemu HBitmap原理

图像与Bitmap类

简单实用算法——位图算法(BitMap)

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计