题记
由于前段时间面试,被面试官问到布隆过滤器的工作原理(是怎么hash处理?),当时只是了解过布隆过滤器并没有答出来
所以今天正好有时间补上这一问题
这也提示我不只能光了解这个东西是什么?
更多的是要去明白为什么需要这个东西? 是如何工作的?
布隆过滤器
什么是位图?
package cn.march.model.bloom_filter;
/**
*
*
* 问题描述:
*
* 利用位图(bitMap)对数组进行排序:
* 位图:
* 简单来讲就是数组存放数据,若存在这个数据则将值置为1或者true
* 比如现在有数组int[]{4,3,2,6} 利用位图来进行存储的话:
* arrays[2] = 1, arrays[3] = 1, arrays[4] = 1, arrays[6] = 1
* 这样可以通过数组index表示值,1代表其值存在
*
*
*/
public class BitMap_Sort {
//测试方法
public static void main(String[] args) {
int[] arrays = new int[]{4,3,2,9,8,10,12};
bitmap_sort(arrays);
for(int array : arrays)
System.out.print(array + " ");
}
/**
* 基本思路:
*
* 首先计算数组的最值:max和min
* 通过max-min+1计算得到位图数组的实际容量new int[max-min+1]
* 通过arrays的值对bit_arrays进行初始化;
* 考虑到arrays的值存在负数,比如:{1,4,-3,2}
* 最小值是-3,所以在对bit_arrays计算的时候:
* for(int array : arrays)
* bit_arrays[array-min]++;
* 这样就消除了负数的影响i
* 之后就根据位图来排序arrays
*
* @param arrays
*/
public static void bitmap_sort(int[] arrays) {
int max = arrays[0];
int min = max;
//求arrays的最值
for(int array : arrays) {
if(max < array)
max = array;
if(min > array)
min = array;
}
//对bit_arrays位图数组进行初始化
int[] bit_arrays = new int[max-min+1];
for(int array : arrays)
bit_arrays[array-min]++;
int index = 0;
//对arrays进行排序;
for(int i = 0; i < bit_arrays.length; i++) {
while(bit_arrays[i] > 0) {
arrays[index] = i + min;
index++;
bit_arrays[i]--;
}
}
}
}
从上面代码我们可以清晰的了解,位图实际就是一个特殊的数组
位图的优势:
- 1.能够快速的判断数据是否存在
- 2.排序
位图的劣势:
- 若数据之间的差距太大(最大值,最小值),耗费空间
比如arrays[1] = 1, arrays[4] = 0, arrays[..] = 0, arrays[9999] = 1;
会造成空间的严重浪费
如java.util.BitSet(实际上就是位图加上位操作)
所以为了解决这个问题,引入hash;
什么是布隆过滤器?
布隆过滤器能够快速的判断数据是否存在集合中
布隆过滤器结合了位图与hash的优势,所以其在空间与时间方面都有巨大的优势
但缺点就是误算率,随着存入的元素数量的增加,误算率随之增加
举个例子:
- 对于数据k,v
- hash(k) = 21,但是在某个情况下hash(v) = 21
- 所以造成hash冲突,这样就导致k,v的误判
这是无可避免的,我们只能想办法降低误判率
我们举个实际例子来说明布隆过滤器是怎么工作的?
假设现在我们需要存储一亿个字符串比如是电子邮件:
dymdmy2120@gmail.com
1亿个邮件地址我们怎么判断我们输入的一个字符串是否存在?
- 创建一个容量8亿的二进制数组bit_set;
- 之后考虑对邮件地址进行hash计算,对于每一个邮件地址X,我们利用8个不同的hash种子(素数)产生8个不同的hash值,将bit_set对应hash值的位置置为1或者为true
- 对这1亿个字符串处理存储好之后(由于数据量较大,我们可以采用分治,hash分割的方式进行处理)
- 现在我们要查询邮件地址Y是否存在这个集合中,我们首先对Y获取其8个hash值,通过hash值判断bit_set对应的值是否为1或者true,若8个hash值对应的值都为1或者true则代表Y存在于集合中
代码如下:
package cn.march.model.bloom_filter;
import java.util.*;
public class BloomFilter {
//布隆过滤器的比特长度
private static final int DEFAULT_SIZE = 2<<24;
//选取质数,能很好的降低错误率
private static final int[] seeds = {3,5,7,11,13,31,37,61};
private static BitSet bits = new BitSet(DEFAULT_SIZE);
//初始化一个哈希类数组
private static SimpleHash[] func = new SimpleHash[seeds.length];
/**
* 向BitSet添加一个字符串
* @param value
*/
private static void addValue(String value) {
for(SimpleHash fun : func) {
bits.set(fun.hash(value), true);
}
}
public static void add(String value) {
if(value != null)
if(contains(value))
throw new RuntimeException("添加的数据已存在!");
else
addValue(value);
}
//判断一个字符串是否在BitSet中是否存在
public static boolean contains(String value) {
if(value == null)
return false;
boolean ret = true;
for(SimpleHash f : func)
ret = ret && bits.get(f.hash(value));
return ret;
}
public static void main(String [] args) {
String value = "dymdmy2120@gmail.com";
for(int i = 0; i < seeds.length; i++) {
func[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]);
}
add(value);
System.out.println(contains(value));
}
/**
* 哈希类:
* 一个cap容量(2 << 24),即BitSet容量(很大)和选定的一个质数
* 通过对应的String value字符串来计算一个对应的hash值
*
* @author antsmarth
*
*/
static class SimpleHash {
//容量(DEFAULT_SIZE)
private int cap;
//质数
private int seed;
public SimpleHash(int cap, int seed) {
this.cap = cap;
this.seed = seed;
}
//计算hash值
public int hash(String value) {
int result = 0;
int len = value.length();
for(int i = 0; i < len; i++) {
result = seed * result + value.charAt(i);
}
return (cap - 1) & result;
}
}
}
好啦,对于布隆过滤器我简单的就描述到这
资料
关于布隆过滤器其实有许多大牛已经探究的很深,所以在这我放几个传送门(好资料共享之):