Guava布隆过滤器(boomfilter)使用简介

1 布隆过滤器简介

布隆过滤器是一种空间利用率较高的概率型数据结构,用来测试一个元素是否在集合中。但是存在一定可能,导致结果误判。即元素不在集合中,查询结果却返回元素在集合中。

布隆过滤器一些的性质

  • 与哈希表不同,布隆过滤器是一个大小固定的过滤器,可以通过任意大的数字来描述集合大小
  • 添加一个元素到集合中永远不会添加失败,但误报率会随着添加元素数量的增多逐渐上升,直到集合中所有位都设置位1
  • 查询一个元素是否存在会产生误报的可能
  • 不应该从集合中删除元素。例如一个元素对应k的hash函数,当我们尝试删除,可能导致将hash值相同的元素也一并删除。

布隆过滤器的工作方式
一个空的布隆过滤器是一个由m个二进制位构成的数组。
在这里插入图片描述
我们需要k个hash函数来计算输入的hash值。当我们向过滤器中添加一个元素事,k个hash函数计算出的索引值 h1(x), h2(x),… hk(x)被设置位1。例如将geeks加入到过滤器中,我们用到3个hash函数,过滤器的长度为10,初始
值被设置为0。哈希函数的计算结果如下:

h1(“geeks”) % 10 = 1
h2(“geeks”) % 10 = 4
h3(“geeks”) % 10 = 7

接着我们将1,4,7位设置位1。
在这里插入图片描述
接着我们将nerd添加到集合中,hash值计算结果如下。

h1(“nerd”) % 10 = 3
h2(“nerd”) % 10 = 5
h3(“nerd”) % 10 = 4

在这里插入图片描述
如果我们想检查geeks是否在集合中,我们以相反的方式进行处理。我们使用h1,h2,h3计算初hash值。如果对应的二进制位都位1,那么可以判定“geeks”可能在集合中。如果任何一位为0,那么"geeks"一定不在集合中。

布隆过滤器结果误判
假如我们希望判定cat是否在集合中,首先计算hash值。得到1,3,7。但1,3,7是之前添加其它单词后设置的结果,我们之前并没有添加cat,因此造成了误判。

h1(“cat”) % 10 = 1
h2(“cat”) % 10 = 3
h3(“cat”) % 10 = 7

如果我们希望降低误判率,我们需要使用更多的hash函数及更大的数组。

布隆过滤器的误报概率
假设数组长度为m,并使用k个hash函数,n是要插入到过滤器中的元素个数,那么误报率的计算如下:
在这里插入图片描述
位数组的大小

如果过滤器中的元素数量已直,期望的误报率位p,那么二进制位数组大小计算公式如下:
在这里插入图片描述
最优哈希函数数量
如果m是数组长度,n是插入的元素个数,k是hash函数的个数,k计算公式如下:
在这里插入图片描述

哈希函数的选择
哈希函数独立且生成的数值均匀分布,并且尽可能的快。比如MurmurHash、Jenkins_hash_function。

2 Guava提供的布隆过滤器

首先添加依赖,版本可根据实际使用来调整。

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>22.0</version>
</dependency>

创建布隆过滤器时要传入期望处理的元素数量,及最期望的误报的概率。如下分别是500和0.01。

BloomFilter<Integer> filter = BloomFilter.create(
  Funnels.integerFunnel(),
  500,
  0.01);

接着向过滤器中插入元素。

filter.put(1);
filter.put(2);
filter.put(3);

我们添加了3个元素,并且定义了最大元素数量为500,因此我们的过滤器将会产生非常准确的结果。我们使用mightContain()方法来测试。

assertThat(filter.mightContain(1)).isTrue();
assertThat(filter.mightContain(2)).isTrue();
assertThat(filter.mightContain(3)).isTrue();
 
assertThat(filter.mightContain(100)).isFalse();

因为布隆过滤器是一种概率型数据结构,因此返回true表示元素有极大的概率存在。当返回false那么表示元素一定不存在。

当我们创建布隆过滤器时,尽可能提供准确的元素数量。否则将会产生较高的误报率。

下面的例子表示集合最多5个元素,这样在实际使用时就会产生很高的误报率。

BloomFilter<Integer> filter = BloomFilter.create(
  Funnels.integerFunnel(),
  5,
  0.01);
 
IntStream.range(0, 100_000).forEach(filter::put);

3 参考资料

[1]布隆过滤器算法介绍,https://www.geeksforgeeks.org/bloom-filters-introduction-and-python-implementation/
[2]Guava中的布隆过滤器,https://www.baeldung.com/guava-bloom-filter

布隆过滤器是一种空间效率很高的概率型数据结构,用于判断一个元素是否在一个集合中。它并不直接支持像数组或链表那样将一个布隆过滤器的内容精确地复制到另一个过滤器,因为它是基于哈希函数和位数组实现的。 如果你想从一个Java布隆过滤器(比如`BloomFilter`、`Guava`s `BitSet BloomFilter`或`java.util.concurrent.CachingConcurrentHashMap.BloomFilter`)复制数据,通常的做法是: 1. **序列化和反序列化**: - 使用Java的`Serializable`接口,可以将布隆过滤器转换为字节数组,然后通过`ObjectOutputStream`或`ObjectInputStream`进行序列化和反序列化操作。这会创建一个新的过滤器,并填充原来的元素信息。 ```java import java.io.*; public class Main { public static void main(String[] args) throws IOException { BloomFilter<String> bloom1 = ...; // 原始布隆过滤器 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(bloom1); byte[] serializedBytes = byteArrayOutputStream.toByteArray(); // 另一处读取并重构新的布隆过滤器 ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serializedBytes); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); BloomFilter<String> bloom2 = (BloomFilter<String>) objectInputStream.readObject(); } } ``` 2. **数据迁移**: - 如果两个布隆过滤器需要共享相同的底层位数组,你可以先创建一个大容量的新过滤器,然后逐个添加原始过滤器中的元素到新过滤器。 ```java BloomFilter<String> bloom1 = ...; BloomFilter<String> bloom2 = new BloomFilter<>(bloom1.getExpectedMaxSize()); // 创建新过滤器 // 将原过滤器的元素添加到新过滤器 for (String element : bloom1) { bloom2.add(element); } ``` 请注意,这种方法并不能保证数据完全一致,因为布隆过滤器存在误报的可能性。此外,如果原始过滤器有删除操作,那么这种复制方法可能会丢失这些删除记录。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值