布隆过滤器

布隆过滤器是一种空间效率极高的概率型数据结构,用于判断一个元素是否在一个集合中,可能会有误判。常用于大数据场景,如黑白名单校验、Redis缓存穿透预防等。其工作原理涉及多个随机哈希函数,一旦元素添加后无法删除,可能导致误判,但可以通过优化初始大小和哈希函数数量来控制误判率。
摘要由CSDN通过智能技术生成

一.简介

布隆过滤器(英语:Bloom Filter)是 1970 年由布隆提出的。它实际上是一个很长的二进制数组(00000000)+一系列随机hash算法映射函数,主要用于判断一个元素是否在集合中。通常我们会遇到很多要判断一个元素是否在某个集合中的业务场景,一般想到的是将集合中所有元素保存起来,然后通过比较确定。链表、树、哈希表等等数据结构都是这种思路。但是随着集合中元素的增加,我们需要的存储空间也会呈现线性增长,最终达到瓶颈。同时检索速度也越来越慢,上述三种结构的检索时间复杂度分别为O(n),O(logn),O(1)。这个时候,布隆过滤器(Bloom Filter)就应运而生

 二.应用场景

1.黑白名单校验

2.解决Redis缓存穿透。

3.在爬虫时,对爬虫网址进行过滤,已经存在布隆中的网址,不在爬取。

4.垃圾邮件过滤,对每一个发送邮件的地址进行判断是否在布隆的黑名单中,如果在就判断为垃圾邮件。

5.大量数据时,判断给定的数据是否在其中。

三.原理

计算公式

  

四. 布隆过滤器为什么不能删除

布隆过滤器的误判是指多个输入经过哈希之后在相同的bit位置1了,这样就无法判断究竟是哪个输入产生的,因此误判的根源在于相同的 bit 位被多次映射且置 1。这种情况也造成了布隆过滤器的删除问题,因为布隆过滤器的每一个 bit 并不是独占的,很有可能多个元素共享了某一位如果我们直接删除这一位的话,会影响其他的元素,布隆过滤器可以添加元素,但是不能删除元素。因为删掉元素会导致误判率增加。

五.特性

1.判断元素存在时有误差:存在表示可能存在,不存在则表示这个元素一定不存在

2.使用的时候尽量不要让实际元素的数量大于初始化布隆过滤器的数量,减少布隆过滤器的扩容

 六.手写布隆过滤器

package com.roar.edr.utils;

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class BloomUtil {
    private static int capacity = 100000000;
    private static double errorP = 0.03;
    private static int[] seeds = {6, 18, 64, 89, 126, 189, 223};
    private static double m;
    private static double k;
    private static double p;
    private static double mem;
    private static byte[] bytes;

    public static void init(){
        m = Math.floor(-(capacity * Math.log(errorP)) / Math.pow(Math.log(2), 2));
        k = Math.floor(Math.log(2) * m / capacity);
        p = Math.pow(1 - (-Math.exp(-Math.log(2))), k);
        mem = m / 8 / 1024 / 1024;
        bytes = new byte[(int) m];
        System.out.println("m = " + m);
        System.out.println("k = " + k);
        System.out.println("p = " + p);
        System.out.println("mem = " + mem);
    }
    public static List<Integer> getHashes(String value){
        List<Integer> hashArray = new ArrayList<>((int) k);
        Random random = new Random(seeds.length);
        for (int i = 0; i < k; i++) {
            int nextInt = random.nextInt();
            int i1 = value.hashCode() ^ (value.hashCode() >>> 16);
            if (!hashArray.contains((Math.abs(nextInt * (int) m- 1) & i1) % (int) m)){
                hashArray.add((Math.abs(nextInt * (int) m- 1) & i1) % (int) m);
            }
        }
        return hashArray;
    }
    public static void add(String value){
        List<Integer> hashes = getHashes(value);
        for (Integer hash : hashes) {
            bytes[hash] = 1;
        }
    }
    public static boolean mightContains(String value){
        List<Integer> hashes = getHashes(value);
        boolean exists = true;
        for (Integer hash : hashes) {
            if (bytes[hash] == 0){
                exists = false;
                break;
            }
        }
        return exists;
    }
    public static void main(String[] args) {
        int _1W = 10000;
        init();
        // 初始化布隆过滤器,设计预计元素数量为100_0000L,误差率为1%
        BloomFilter<CharSequence> bloomFilter = BloomFilter.create(Funnels.stringFunnel(StandardCharsets.UTF_8), 100 * _1W, 0.03);
        int n = 100 * _1W;
        for (int i = 0; i < n; i++) {
            bloomFilter.put(String.valueOf(i));
        }
        for (int i = 0; i < n; i++) {
            System.out.println("getHashes(String.valueOf(i)) = " + getHashes(String.valueOf(i)));
            add(String.valueOf(i));
        }
        int count = 0;
        int count1 = 0;
        for (int i = n + 1; i <= n + 100000; i++) {
            if (bloomFilter.mightContain(String.valueOf(i))){
                count ++;
            }
        }
        for (int i = n + 1; i <= n + 100000; i++) {
            if (mightContains(String.valueOf(i))){
                count1 ++;
            }
        }
        System.out.println("count1 = " + count1);
        System.out.println("count = " + count);
        System.out.println("过滤器误判率:" + 1.0 * count / n * 100);
        System.out.println("过滤器误判率:" + 1.0 * count1 / n * 100);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值