提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
布隆过滤器的原理
布隆过滤器使用一个位数组(bit array)来表示元素的存在情况。当元素被加入布隆过滤器时,从元素中提取若干个特征(或哈希函数),并将位数组中对应的特征置为1。当某个元素被查询时,从元素中提取相同的特征,并检查位数组中对应特征的值是否都为1。如果所有特征的值都为1,则表明元素可能存在于布隆过滤器中;如果有任意一个特征的值为0,则表明元素肯定不存在于布隆过滤器中。
具体来说,布隆过滤器需要完成以下几个操作:
-
初始化:创建大小为 m 的位数组,将所有位都置为0。
-
插入元素:对于要插入的元素,计算 k 个哈希值,将位数组中对应的 k 个位置置为1。
-
检查元素是否存在:对于要检查的元素,计算 k 个哈希值,检查位数组中对应的 k 个位置是否都为1。如果都为1,则表明元素可能存在于布隆过滤器中;如果有任意一个位置为0,则表明元素一定不存在于布隆过滤器中。
由于哈希函数的不确定性和位数组在处理大量元素时会发生哈希冲突的情况,因此布隆过滤器可能会出现误判的情况(将不存在的元素误判为存在)。为了减少误判率,我们可以增加位数组的大小 m 或者使用更多的哈希函数 k,但是这会增加计算和存储的开销。
一、布隆过滤器为什么被推崇?
布隆过滤器是一种高效的数据结构,它以空间效率和查询速度为代价,提供了近似的成员存在判断功能。布隆过滤器的核心在于其使用的哈希算法,以下是布隆过滤器被推崇的几个原因:
-
高效的查询速度:布隆过滤器通过哈希算法将元素映射到位图中的位置,查询时只需进行几次哈希计算即可判断元素是否存在于布隆过滤器中。因为哈希算法是常数时间的操作,布隆过滤器的查询速度非常快,不受数据规模的影响。
-
极高的空间效率:布隆过滤器使用位图来表示元素的存在情况,相比存储实际数据对象本身,所需的存储空间大大减少。并且布隆过滤器的空间占用是固定的,不会随着元素数量的增加而增加。
-
支持高效的插入和删除操作:布隆过滤器的插入和删除操作都只需进行几次哈希计算,并在位图中设置对应的位值。这些操作很快,不会随着数据规模的变化而增加。
-
良好的可扩展性:布隆过滤器的空间占用是固定的,不会随着元素数量的增加而增加。当需要处理大量元素时,可以使用多个布隆过滤器并进行合并,以满足需求。
尽管布隆过滤器具有一定的误判率(可能会把不存在的元素判断为存在),但在很多实际应用中,其优势远远超过了这个缺点。在需要高效地判断元素是否存在的场景中,布隆过滤器是一种非常有用的数据结构。它被广泛应用于缓存、数据库查询优化、网络爬虫等领域,能够大大减少对底层存储的访问压力,提高系统的响应速度。
二、Java一般如何来实现
在 Java 中,一般可以通过以下步骤来实现一个布隆过滤器:
-
选择合适的位图数据结构:布隆过滤器的核心是位图,用来表示元素的存在情况。Java 中,可以使用
java.util.BitSet
类来表示位图。BitSet
是一个包含了固定大小的位值的数组,可以通过位操作来设置、获取和检查位值。 -
选择合适的哈希算法:布隆过滤器依赖哈希算法对元素进行映射到位图的位置。在 Java 中,可以使用一些常用的哈希函数,如
java.lang.Object
类的hashCode()
方法、java.util.Objects.hash()
方法、java.util.hashing.MurmurHash
等。根据应用场景的需要,也可以选择其他哈希算法。 -
初始化布隆过滤器:创建一个位图并根据预估的数据规模和期望的误判率来确定位图的大小和哈希函数的数量。通常,位图的大小(位的数量)会随着数据规模的增加而增加,而哈希函数的数量则根据预估的数据规模和期望的误判率进行计算。
-
插入元素:对待插入的元素进行多次哈希计算,将得到的哈希值对位图的对应位置进行置位操作。
-
检查元素是否存在:对待检查的元素进行多次哈希计算,检查得到的所有位置是否都被置位。如果有任何一个位置未被置位,则可以确定元素不存在于布隆过滤器中;如果所有位置都被置位,则元素可能存在于布隆过滤器中。
需要注意的是,Java 中并没有内置的布隆过滤器实现。因此,上述步骤的具体实现方式可能因个人需求和偏好而有所不同。你可以根据具体的场景需求,参考这些步骤实现自己的布隆过滤器,或者使用第三方库(如 Guava
、BloomFilter
等)提供的布隆过滤器实现。这些库会更加方便和易于使用,并提供了一些额外的功能和配置选项。
三、Google Guava的实现原理
下载地址:https://github.com/google/guava
3.1、核心类BloomFilter
/** BloomFilter的位集(不一定是2的幂!) */
private final LockFreeBitArray bits;
/** 每个元素的哈希数 */
private final int numHashFunctions;
/** 将元素(类型为 T)转换为字节数组。该类是布隆过滤器中的 Funnel 接口的实现或扩展。 */
private final Funnel<? super T> funnel;
/** 我们用来将元素T映射到{@code numHashFunctions}位索引的策略。*/
private final Strategy strategy;
/** Creates a BloomFilter. */
private BloomFilter(
LockFreeBitArray bits, int numHashFunctions, Funnel<? super T> funnel, Strategy strategy) {
checkArgument(numHashFunctions > 0, "numHashFunctions (%s) must be > 0", numHashFunctions);
checkArgument(
numHashFunctions <= 255, "numHashFunctions (%s) must be <= 255", numHashFunctions);
this.bits = checkNotNull(bits);
this.numHashFunctions = numHashFunctions;
this.funnel = checkNotNull(funnel);
this.strategy = checkNotNull(strategy);
}
总结
先写这么多,后续再补充