布隆过滤器和hashmap布隆过滤器和hash区别
作者:XiaoMing 发布时间:2023-10-25 栏目: 财经知识 0浏览
老铁们,大家好,相信还有很多朋友对于布隆过滤器和hashmap和布隆过滤器和hash区别的相关问题不太懂,没关系,今天就由我来为大家分享分享布隆过滤器和hashmap以及布隆过滤器和hash区别的问题,文章篇幅可能偏长,希望可以帮助到大家,下面一起来看看吧!
本文目录
布隆过滤器数据结构学的到底是什么,和算法的关系BitMap原理与实现布隆过滤器和hashmap的区别布隆过滤器[TOC]
通过解决方案:
Java中如将数据存储在内存中,最简单的算法结构是HashMap。通过HashMap判断key是否存在,来判断数据是否存在。通过hash算法查找元素,时间复杂度基本是O(1)(可能存在hash冲突后转换成链表或红黑树的情况,时间复杂度的影响可以忽略)。
使用HashMap速度很快,存储简单,绝大部分场景可以使用。但是HashMap占用的空间比较大:
为什么出现布隆过滤器:
举例:
如1000万个Integer存储在内存中,占用空间为:4x32x10000000位,即1220兆。如布隆过滤器通过4字节存储(布隆过滤器通过多次hash对数据计算后-->几次hash根据数据量指定,得到多个数据,占用多个位),则占用空间为610M。比原有空间少一半。
个人觉得,此比较在字符等的比较中尤为有效。
一个字符串多个字符,根据编码方式,一个字符两个或三个字节,如10个字符,字符串存储占用20个字节,还有相关字符串相关的类信息的内存占用。
位存储,根据数据量的大小,hash的位数,灵活计算。如4个字节,则是原hashMap占用空间的五分之一。
(1)定义字节向量
先定义一个指定长度的字节数组(字节数组,数组内每个元素的值)。
如长度为8(一个字节大小),默认所有元素值均为0,如下:
(2)计算哈希值
将要写入过滤器的数据,根据一定数量的哈希函数,得到多个哈希值,再依次判断每个哈希值对应的索引。
如使用3个哈希函数,计算得到3个哈希值,判定哈希值对应的字节向量为为1,3,7。
(3)更新字节向量
将计算出的字节向量的索引,对应的字节向量中的元素值更高为1(无论之前为0或者为1,均更改为1)。如下:
(1)计算哈希值
将要判断过滤器中是否存在的数据,根据一定数量的哈希函数,得到多个哈希值,再依次判断每个哈希值对应的索引。
如使用3个哈希函数,计算得到3个哈希值,判定哈希值对应的字节向量为为1,3,7。
注意:哈希函数的判断方式和计算索引的方式,需和写入数据时完全一致。
(2)判断是否存在
如原字节数组中,对应1,3,7中存在的元素的值都为1。则判定为此元素可能存在,但凡有一个元素的值不为1,则判定此元素一定不存在。
布隆过滤器,主要需实现的目标是,在指定的数据个数范围内,满足误判率在设定的范围内,误判率太高的话,无法起到过滤数据的情况,误判率不能为0。
因此需要计算两个数据来满足存储数据的个数和误判率:
使用布隆过滤器的决定性因素之一,就是此算法插入数据和查询数据的速度必须非常快。因此在对数据进行哈希运算的时候,需选择计算快的哈希算法。
而且,写入数据以及查询数据的哈希算法,顺序和算法都需完全一致。
待完善。。。。。
可以通过google的guava,在内存中轻松实现布隆过滤器。
无需手动计算满足字节数组的长度和哈希个数,只需要输入拟输入数据的个数和期望误判率即可。
不输入期望误判率的情况下,误判率为0.03,即100个非范围内的数据进行校验时,约三个数据会判定为存在。
多次执行,结果一致,根据结果判定:
内存的存储存在局限性,可以使用redis中的bitMap来实现字节数组的存储。
使用redis实现布隆过滤器。需要根据公式,手动计算字节数组的长度和哈希的个数。
实现过程,待完善。。。。。。
数据结构学的到底是什么,和算法的关系本人乃一个数据痴迷者,在计算机的道路上,也是一个数据结构的痴迷者,现在大学里面和同学搞开发也痴迷于数据库,我就我个人的理解给你谈一谈:
首先,数据结构是一门计算机语言学的基础学科,它不属于任何一门语言,其体现的是几乎所有标准语言的算法的思想。
上面的概念有一些模糊,我们现在来具体说一说,相信你门的数据结构使用的是一门具体的语言比如C/C++语言来说明,那是为了辅助的学习数据结构,而数据结构本身不属于任何语言(相信你把书上的程序敲到电脑里面是不能通过的吧,其只是描述了过程,要调试程序,还需要修改和增加一些东西)。你们的书上开始应该在讲究数据的物理存储结构/逻辑存储结构等概念,说明数据结构首先就是“数据的结构”,在内存上的存储方式,就是物理的存储结构,在程序使用人员的思想上它是逻辑的,比如:
你们在C/C++中学习到链表,那么链表是什么一个概念,你们使用指针制向下一个结点的首地址,让他们串联起来,形成一个接一个的结点,就像显示生活中的火车一样。而这只是对于程序员的概念,但是在内存中存储的方式是怎样的那?对于你程序员来说这是“透明”的,其内部分配空间在那里,都是随机的,而内存中也没有一个又一根的线将他们串联起来,所以,这是一个物理与逻辑的概念,对于我们程序员只需要知道这些就可以了,而我们主要要研究的是“逻辑结构”。
我可以给你一个我自己总结的一个概念:所有的算法必须基于数据结构生存。也就是说,我们对于任何算法的编写,必须依赖一个已经存在的数据结构来对它进行操作,数据结构成为算法的操作对象,这也是为什么算法和数据结构两门分类不分家的概念,算法在没有数据结构的情况下,没有任何存在的意义;而数据结构没有算法就等于是一个尸体而没有灵魂。估计这个对于算法的初学者可能有点晕,我们在具体的说一些东西吧:
我们在数据结构中最简单的是什么:我个人把书籍中线性表更加细化一层(这里是为了便于理解在这样说的):单个元素,比如:inti;这个i就是一个数据结构,它是一个什么样的数据结构,就是一个类型为int的变量,我们可以对它进行加法/减法/乘法/除法/自加等等一系列操作,当然对于单个元素我们对它的数据结构和算法的研究没有什么意义,因为它本来就是原子的,某些具体运算上可能算法存在比较小的差异;而提升一个层次:就是我们的线性表(一般包含有:顺序表/链表)那么我们研究这样两种数据结构主要就是要研究它的什么东西那?一般我们主要研究他们以结构为单位(就是结点)的增加/删除/修改/检索(查询)四个操作(为什么有这样的操作,我在下面说到),我们一般把“增加/删除/修改”都把它称为更新,对于一个结点,若要进行更新一类的操作比如:删除,对于顺序表来说是使用下标访问方式,那么我们在删除了一个元素后需要将这个元素后的所有元素后的所有元素全部向前移动,这个时间是对于越长的顺序表,时间越长的,而对于链表,没有顺序的概念,其删除元素只需要将前一个结点的指针指向被删除点的下一个结点,将空间使用free()函数进行释放,还原给操作系统。当执行检索操作的时候,由于顺序表直接使用下标进行随机访问,而链表需要从头开始访问一一匹配才可以得到使用的元素,这个时间也是和链表的结点个数成正比的。所以我们每一种数据结构对于不同的算法会产生不同的效果,各自没有绝对的好,也没有绝对的不好,他们都有自己的应用价值和方式;这样我们就可以在实际的项目开发中,对于内部的算法时间和空间以及项目所能提供的硬件能力进行综合评估,以让自己的算法能够更加好。
(在这里只提到了基于数据结构的一个方面就是:速度,其实算法的要素还应该包括:稳定性、健壮性、正确性、有穷性、可理解性、有输入和输出等等)
为什么要以结点方式进行这些乱七八糟的操作那?首先明确一个概念就是:对于过程化程序设计语言所提供的都是一些基础第一信息,比如一些关键字/保留字/运算符/分界符。而我们需要用程序解决现实生活中的问题,比如我们要程序记录某公司人员的情况变化,那么人员这个数据类型,在程序设计语言中是没有的,那么我们需要对人员的内部信息定义(不可能完全,只是我们需要那些就定义那些),比如:年龄/性别/姓名/出生日期/民族/工作单位/职称/职务/工资状态等,那么就可以用一些C/C++语言描述了,如年龄我们就可以进行如下定义:
intage;/*age变量,表示人员公司人员的年龄*/
同理进行其他的定义,我们用结构体或类把他们封装成自定义数据类型或类的形式,这样用他们定义的就是一个人的对象的了,它内部包含了很多的模板数据了。
我就我个人的经历估计的代码量应该10000以内的(我个人的经理:只是建议,从你的第一行
代码开始算,不论程序正确与否,不论那一门语言,作为一个标准程序员需要十万行的代码的功底(这个是我在大学二年级感觉有一定时候的大致数据,不一定适合其他人),而十万行代码功底一般需要四门基础远支撑,若老师没有教,可以自学一些语言)。BitMap原理与实现比较经典的问题是:在只能够使用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.确定该数处于数组中的哪个元素的位上
intwordIndex=bitIndex>>5;
因为我用的是int[]实现,所以这里右移5位(2^5=32)
2.确定相对于该元素中的位置偏移
intbitPosition=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的思想,我们可以将存储空间进行压缩,而且可以对数字进行快速排序、去重和查询的操作。
BloomFliter是Bit-map思想的一种扩展,它可以在允许低错误率的场景下,大大地进行空间压缩,是一种拿错误率换取空间的数据结构
当一个元素加入布隆过滤器中的时候,会进行哪些操作:
当我们需要判断一个元素是否存在于布隆过滤器的时候,会进行哪些操作:
然后,一定会出现这样一种情况:不同的字符串可能哈希出来的位置相同(可以适当增加位数组大小或者调整我们的哈希函数来降低概率),因此:布隆过滤器可能会存在误判的情况
总结来说就是:布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。
BloomFilter的应用:常用于解决缓存穿透等场景。
布隆过滤器和hashmap的区别但是布隆过滤器的缺点和优点一样明显。误算率是其中之一。随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表足矣。另外,一般情况下不能从布隆过滤器中删除元素。我们很容易想到把位列阵变成整数数组,每插入一个元
END,本文到此结束,如果可以帮助到大家,还望关注本站哦!