我正在参加「掘金·启航计划」
当需要判断某个元素是否处于某个集合中时,为了快速查询可以使用散列表Map来进行存储元素
散列表本身就是空间换时间的数据结构,当元素数量多且元素所占空间非常大时,使用散列表会占用大量空间
比如爬虫要爬取很多网络地址,想要过滤掉已经访问过的地址时,可以使用布隆过滤器
布隆过滤器
什么是布隆过滤器呢?布隆过滤器并不是LOL中布隆拿着的大盾牌
布隆过滤器由位数组与多个哈希函数构成
当将某个元素加入集合时,使用多个哈希函数对该元素得到索引位置,将位数组该索引位置变为1
判断该元素是否加入集合时,也使用多个哈希函数对该元素得到索引位置,判断位数组中索引位置是否为1,都为1说明加入过集合,否则未加入
由于索引位置上的1可能是其他元素设置的,布隆过滤器存在一定误差, 误差率随着位数组长度的增大而减小
布隆过滤器的时间复杂度为O(n个哈希函数),空间复杂度为O(位数组长度)
实现
定义类
定义布隆过滤器类
使用long数组充当位数组,hashSize记录hash函数数量,泛型存储对应类型元素
public class BloomFilter<T> {
/**
* 位数组
*/
private long[] bit;
/**
* 一共有多少个二进制位
*/
private int bitSize;
/**
* hash函数数量
*/
private int hashSize;
}
添加元素
put添加元素,先对元素进行检查,再循环使用多个哈希函数生成索引设置为1
/**
* 添加元素
*
* @param value
* @return 发生变化返回true,未发生变化返回false
*/
public boolean put(T value) {
//空指针检查
nullCheck(value);
// 利用元素生成2个整数,将这2个整数经过hash函数生成的索引赋值为1
int hash1 = value.hashCode();
int hash2 = hash1 >>> 16;
boolean result = false;
for (int i = 1; i <= hashSize; i++) {
//相当于hash函数 生成一个二进位的索引index
int combinedHash = hash1 + (i * hash2);
if (combinedHash < 0) {
combinedHash = ~combinedHash;
}
int index = combinedHash % bitSize;
// 设置index位置的二进位为1
if (set(index)) {
result = true;
}
}
return result;
}
/**
* 设置index位置的二进位为1
*
* @param index
* @return
*/
private boolean set(int index) {
//找到对应的long值value 用value | (1<<k)
//找到索引所在数组位置,一个long是64位
long value = bit[index / Long.SIZE];
//获得long值对应要修改为1的索引 (0-63)
int k = index % Long.SIZE;
bit[index / Long.SIZE] = value | (1 << k);
// 101010101010010001
// | 000000000000000100 1 << k
// 101010111010010101
//如果原来value与1<<k对应的位置是0则返回true,是1则返回false
return (value & (1 << k))==0 ;
}
判断元素是否存在
/**
* 判断元素value是否存在
* @param value
* @return false:一定不存在 true:可能存在
*/
public boolean contains(T value) {
nullCheck(value);
//先按照Put前面的步骤找到value对应的索引位置
int hash1 = value.hashCode();
int hash2 = hash1 >>> 16;
for (int i = 1; i <= hashSize; i++) {
int combinedHash = hash1 + (i * hash2);
if (combinedHash < 0) {
combinedHash = ~combinedHash;
}
int index = combinedHash % bitSize;
if (!get(index)) {
return false;
}
}
return true;
}
/**
* 判断index位置的二进制位数是否为1
* @param index
* @return
*/
private boolean get(int index){
long value = bit[index / Long.SIZE];
int k = index % Long.SIZE;
//如果value与1<<k对应的位置是1则返回true,是0则返回false
return (value & (1 << k)) != 0;
}
总结
布隆过滤器由位数组与多个哈希函数构成
布隆过滤器适用于判断多元素是否加入过集合,特点是占用空间小, 速度快,可能出现误差
本篇文章将被收入数据结构专栏,觉得不错感兴趣的同学可以收藏专栏哟~
觉得菜菜写的不错,可以点赞、关注支持哟~
有什么问题可以在评论区交流喔~