位图&布隆过滤器

位图&布隆过滤器

在这里插入图片描述

个人主页:顾漂亮

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. 思想:将一个元素用多个哈希函数映射到一个位图中,被映射的位置比特位一定为1,没有被映射的一定为0
  2. 查找策略:分别计算每一个哈希值对应的比特位存储是否为0,只要有一个为0,那么这个元素一定不存在,否则可能存在
  3. 注意:布隆过滤器只能确定元素一定不存在和元素可能存在,因为有些哈希函数存在误判

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布隆过滤器优点

  1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关
  2. 哈希函数相互之间没有关系,方便硬件并行运算(哈希函数都是随机生成)
  3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
  4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势
  5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能
  6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算

2.8布隆过滤器缺陷

  1. 误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再建立一个白

名单,存储可能会误判的数据)

  1. 不能获取元素本身
  2. 一般情况下不能从布隆过滤器中删除元素
  3. 如果采用计数方式删除,可能会存在计数回绕问题

2.9使用场景:

1.垃圾邮件过滤
2. 适用于海量数据并且能接受误判率的插入和查找

3.解决数据库缓存击穿,黑客攻击服务器时,会构建大量不存在于缓存中的key向服务器发起请求,在数据量足够大的时候,频繁的数据库查询会导致挂机。

4.秒杀系统,查看用户是否重复购买。

总结:

位图布隆过滤器
适合处理整数、大量数据,可以查找、排序、去重处理非整数,使用哈希函数+位图来操作,查找时间复杂度和哈希函数个数有关,无法存储元素本身
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值