布隆过滤器的概念
布隆过滤器(英语:Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
布隆过滤器的基本思想
如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定。链表、树、散列表(又叫哈希表,Hash table)等等数据结构都是这种思路。但是随着集合中元素的增加,我们需要的存储空间越来越大。同时检索速度也越来越慢,上述三种结构的检索时间复杂度分别为 O(n),O(logn),O(n/k)。布隆过滤器的原理是,当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。
- 一个Bloom Filter是基于一个m位的位向量(b1…bm),这些位向量的初始值为0。另外,还有一系列的hash函数(h1…hk),这些hash函数的值域属于1~m。下图是一个bloom filter插入x,y,z幷判断某个值w是否在该数据集的示意图:
实现一个布隆过滤器
public class BloomFilter {
//位图的长度
public static final int NUM_SLOTS = 1024 * 1024 * 8;
//hash函数的个数,一个hash函数的结果用于标记一个位
public static final int NUM_HASH = 8;
//位图
private BigInteger bitmap = new BigInteger("0");
public static void main(String[] args) {
//测试代码
BloomFilter bf = new BloomFilter();
ArrayList<String> contents = new ArrayList<>();
contents.add("sldkjelsjf");
contents.add("ggl;ker;gekr");
contents.add("wieoneomfwe");
contents.add("sldkjelsvrnlkjf");
contents.add("ksldkflefwefwefe");
for (int i = 0; i < contents.size(); i++) {
bf.addElement(contents.get(i));
}
System.out.println(bf.check("sldkjelsvrnlkjf"));
System.out.println(bf.check("sldkjelnlkjf"));
System.out.println(bf.check("ggl;ker;gekr"));
}
/**将message+n映射到0~NUM_SLOTS-1之间的一个值*/
private int hash(String message, int n) {
message = message + String.valueOf(n);
try {
//将任意输入映射成128位(16个字节)整数的hash函数
MessageDigest md5 = MessageDigest.getInstance("md5");
byte[] bytes = message.getBytes();
md5.update(bytes);
byte[] digest = md5.digest();
//至此,获得message+n的md5结果(128位整数)
BigInteger bi = new BigInteger(digest);
return Math.abs(bi.intValue()) % NUM_SLOTS;
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(BloomFilter.class.getName()).log(Level.SEVERE, null, ex);
}
return -1;
// return (int)Math.abs(HashFunctions.bernstein(message,NUM_SLOTS));
}
/*处理原始数据
* 1.hash1(msg)标注一个位…… hash的值域0~NUM_SLOTS-1
* */
public void addElement(String message) {
for (int i = 0; i < NUM_HASH; i++) {
//代表了hash1,hash2……hash8
int hashcode = hash(message, i);
//结果,用于标注位图的该位为1
if (!bitmap.testBit(hashcode)) {
//如果还不为1,标注位图的该位为1
bitmap = bitmap.or(new BigInteger("1").shiftLeft(hashcode));
}
}
}
public boolean check(String message) {
for (int i = 0; i < NUM_HASH; i++) {
int hashcode = hash(message, i);
//hashcode代表一个位置
if (!this.bitmap.testBit(hashcode)) {
//如果位图的该位为0,那么message一定不存在
return false;
}
}
//不精确,有可能误判
return true;
}
}