位图&布隆过滤器
文章目录
1.位图
1.1概念
位图,就是用每一位来存放某种状态,适用于海量数据,整数、数据无重复的场景。通常是用来判断某个数据存不存在的。
位图示例:
1.2位图的实现
public class MyBitSet {
public byte[] elem;
public int usedSize;
public MyBitSet(){
//默认只给一个大小
elem = new byte[1];
}
//n为一组数据中最大值
public MyBitSet(int n){
elem = new byte[n / 8 + 1];//注意向上取整,否则会出现数据缺失的情况,虽然多出一个字节大小,但是无伤大雅
}
//向位图中添加元素
public void set(int val){
if(val < 0){
throw new IndexOutOfBoundsException();
}
//扩容,如果elem中空间不足,及时扩容
if(index > elem.length-1){
elem = Arrays.copyOf(elem, index + 1);
}
int index = val / 8;
int bitIndex = val % 8;
elem[index] |= (1 << bitIndex);
usedSize++;
}
//判断位图中是否存在元素
public boolean get(int val){
if(val < 0){
throw new IndexOutOfBoundsException();
}
int index = val / 8;
int bitIndex = val % 8;
if((elem[index] & (1 << bitIndex))!= 0){
return true;
}
return false;
}
//将位图中相应元素位置置为0
public void reSet(int val){
if(val < 0){
throw new IndexOutOfBoundsException();
}
int index = val / 8;
int bitIndex = val % 8;
elem[index] &= ~(1 << bitIndex);
usedSize--;
}
//返回当前有多少数字存储在位图中
public int size(){
return usedSize;
}
}
1.3拓展:如何使用位图进行数据的排序
- 第一步:先将数据按照规则存入位图
- 第二步:依次从位图中读取元素
public static void main(String[] args) {
//位图排序
int[] array = {1,3,2,13,10,3,14,18,3, 1, 3};
MyBitSet myBitSet = new MyBitSet(18);
for (int i = 0; i < array.length; i++) {
myBitSet.set(array[i]);
}
//排序
for (int i = 0; i < myBitSet.elem.length; i++) {
for (int j = 0; j < 8; j++) {
if((myBitSet.elem[i] & (1 << j)) != 0){
System.out.print(i * 8 + j + " ");
}
}
}
}
1.4位图的应用
- 在海量数据中快速查找某个数据是否在一个集合中
- 可以通过位图进行排序,并且可以去重,因为在位图中,一个bit位只能是0或者1,无法存储重复数据
- 求两个集合的交集(&)、并集 (I)
- 在操作系统中可以快速访问磁盘块
2.布隆过滤器
2.1布隆过滤器的提出
在日常生产场景中,我们经常需要判断一个元素是否存在一个集合中。比如,在网络爬虫里,一个网址是否被访问过。最直接的办法就是将集合中的全部元素存储在计算机中,依次进行查找。
一般计算机中的集合是用哈希表(hash table)
来存储的。它的好处是快速准确、缺点就是费存储空间。当集合比较小的时候,这个问题不显著,如果面对海量数据,哈希表存储效率低的问题就会显现出来。
- 哈希表存储:缺点:存储效率一般只有50%,面对海量数据时候浪费空间
- 位图存储:缺点:位图一般只能处理整形,如果内容编号是字符串,无法处理
- 将哈希表和位图结合,即布隆过滤器
2.2布隆过滤器的概念
布隆过滤器是由布隆这位科学家在1970年提出的一种紧凑型的、比较巧妙的概率性数据结构,特点是高效插入和查询,可以告诉我们某样东西一定存在或者可能存在,其原理是利用多个哈希函数,将一个数据映射到位图结构中。此种方式不但可以提升查询效率,也可以节省大量内存空间
2.3布隆过滤器的插入
- 向布隆过滤器中插入:
baidu
- 向布隆过滤器中插入:
tencent
2.4布隆过滤器的查找
- 思想:将一个元素用多个哈希函数映射到一个位图中,被映射的位置比特位一定为1,没有被映射的一定为0
- 查找策略:分别计算每一个哈希值对应的比特位存储是否为0,只要有一个为0,那么这个元素一定不存在,否则可能存在
- 注意:布隆过滤器只能确定元素一定不存在和元素可能存在,因为有些哈希函数存在误判
2.5布隆过滤器实现
package BloomFilter;
import java.util.BitSet;
//生成不同的哈希函数
class SimpleHash{
public int cap;//当前容量
public int seed;//随机种子
public SimpleHash(int cap, int seed){
this.cap = cap;
this.seed = seed;
}
//根据不同seed,创建不同的哈希函数,可以得到不同的哈希值
public int hash(String key){
int h;
//(n - 1) & hash
return (key == null) ? 0 : (seed * (cap-1)) & ((h = key.hashCode()) ^ (h >>> 16));//涉及哈希函数底层源码,可以不深追究
}
}
public class MyBloomFiler {
public static final int DEFAULT_SIZE = 1 << 24;//位图默认大小
public static final int[] seeds = {5,7,11,13,27,33};//随机种子
public BitSet bitSet;//位图存储元素
public SimpleHash[] simpleHashes;//存储哈希函数
public MyBloomFiler(){
bitSet = new BitSet(DEFAULT_SIZE);//初始化位图
simpleHashes = new SimpleHash[seeds.length];//初始化存放哈希函数的数组
for (int i = 0; i < simpleHashes.length; i++) {//依次为哈希函数数组赋值
simpleHashes[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]);
}
}
//添加元素
public void add(String val){
for (SimpleHash s: simpleHashes) {
int index = s.hash(val);
bitSet.set(index);
}
}
//查看是否包含val元素 -- 存在误判现象
public boolean contains(String val){
for (SimpleHash s:simpleHashes) {
int index = s.hash(val);
//只要有一个为0,那么一定不存在
if(!bitSet.get(index)){
return false;
}
}
return true;
}
}
2.6布隆过滤器删除
布隆过滤器不可以直接删除,因为在删除一个元素时候,可能会影响其它元素
- 一种删除的方案:将过滤器中每一个比特位扩展为一个小的计数器,插入元素时候给计数器加一,删除元素时候,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作
缺点:
- 无法确认元素是否真正在过滤器中**【存在误判】**
- 存在计数回绕**【数据溢出】**:例如整形int,其属于范围为 - 128 ~ 127,可能会形成一个闭环
2.7布隆过滤器优点
- 增加和查询元素的时间复杂度为:
O(K)
, (K为哈希函数的个数,一般比较小),与数据量大小无关 - 哈希函数相互之间没有关系,方便硬件并行运算(哈希函数都是随机生成)
- 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
- 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势
- 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能
- 使用同一组散列函数的布隆过滤器可以进行交、并、差运算
2.8布隆过滤器缺陷
- 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再建立一个白
名单,存储可能会误判的数据)
- 不能获取元素本身
- 一般情况下不能从布隆过滤器中删除元素
- 如果采用计数方式删除,可能会存在计数回绕问题
2.9使用场景:
1.垃圾邮件过滤
2. 适用于海量数据并且能接受误判率的插入和查找3.解决数据库缓存击穿,黑客攻击服务器时,会构建大量不存在于缓存中的key向服务器发起请求,在数据量足够大的时候,频繁的数据库查询会导致挂机。
4.秒杀系统,查看用户是否重复购买。
总结:
位图 | 布隆过滤器 |
---|---|
适合处理整数、大量数据,可以查找、排序、去重 | 处理非整数,使用哈希函数+位图来操作,查找时间复杂度和哈希函数个数有关,无法存储元素本身 |