浅谈BloomFilter【下】用Java实现BloomFilter
通过前一篇文章的学习,对于 BloomFilter 的概念和原理。以及误报率等计算方法都一个理性的认识了。
在这里,我们将用 Java'实现一个简单的 BloomFilter 。
package pri.xiaoye.day1029; import java.io.Serializable;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.BitSet;
import java.util.Collection; /**
* 布隆过滤器的实现类
* 定义:http://en.wikipedia.org/wiki/Bloom_filter
*
* @param <E>
* E指定了要插入过滤器中的元素的类型。eg,String Integer
* @author Magnus Skjegstad <magnus@skjegstad.com>
* @translator xiaoye
*/
public class BloomFilter<E> implements Serializable { private static final long serialVersionUID = -2326638072608273135L;
private BitSet bitset;
private int bitSetSize;
private double bitsPerElement;
private int expectedNumberOfFilterElements;//可以加入的元素的最大个数
private int numberOfAddedElements;//过滤器容器中元素的实际数量
private int k; // 哈希函数的个数 static final Charset charset = Charset.forName("UTF-8");//存储哈希值的字符串的编码方式 static final String hashName = "MD5"; //在大多数情况下,MD5提供了较好的散列准确度。如有必要,可以换成 SHA1算法
static final MessageDigest digestFunction;//MessageDigest类用于为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法
static { // 初始化 MessageDigest 的摘要算法对象
MessageDigest tmp;
try {
tmp = java.security.MessageDigest.getInstance(hashName);
} catch (NoSuchAlgorithmException e) {
tmp = null;
}
digestFunction = tmp;
} /**
* 构造一个空的布隆过滤器. 过滤器的长度为c*n
* @param c
* 表示每一个元素占有多少位
* @param n
* 表示过滤器能加入的最大元素数量
* @param k
* 表示须要使用的哈希函数的个数
*/
public BloomFilter(double c, int n, int k) {
this.expectedNumberOfFilterElements = n;
this.k = k;
this.bitsPerElement = c;
this.bitSetSize = (int) Math.ceil(c * n);
numberOfAddedElements = 0;
this.bitset = new BitSet(bitSetSize);
} /**
* 构造一个空的布隆过滤器。最优哈希函数的个数将由过滤器的总大小和期望元素个数来确定。
*
* @param bitSetSize
* 指定了过滤器的总大小
* @param expectedNumberOElements
* 指定了过滤器能加入的最大的元素数量
*/
public BloomFilter(int bitSetSize, int expectedNumberOElements) {
this(bitSetSize / (double) expectedNumberOElements, expectedNumberOElements, (int) Math.round((bitSetSize / (double) expectedNumberOElements)* Math.log(2.0)));
} /**
* 通过指定误报率来构造一个过滤器。 * 每一个元素所占的位数和哈希函数的数量会依据误报率来得出。
*
* @param falsePositiveProbability
* 所期望误报率.
* @param expectedNumberOfElements
* 要加入的元素的数量
*/
public BloomFilter(double falsePositiveProbability, int expectedNumberOfElements) {
this(Math.ceil(-(Math.log(falsePositiveProbability) / Math.log(2)))/ Math.log(2), // c = k/ln(2)
expectedNumberOfElements,
(int) Math.ceil(-(Math.log(falsePositiveProbability) / Math.log(2)))); // k = ln(2)m/n
} /**
* 依据旧过滤器的数据。又一次构造一个新的过滤器
*
* @param bitSetSize
* 指定了过滤器所需位的大小
* @param expectedNumberOfFilterElements
* 指定了过滤器所能加入的元素的最大数量
* to contain.
* @param actualNumberOfFilterElements
* 指定了原来过滤器的数据的数量
* <code>filterData</code> BitSet.
* @param filterData
* 原有过滤器中的BitSet对象
*/
public BloomFilter(int bitSetSize, int expectedNumberOfFilterElements,
int actualNumberOfFilterElements, BitSet filterData) {
this(bitSetSize, expectedNumberOfFilterElements);
this.bitset = filterData;
this.numberOfAddedElements = actualNumberOfFilterElements;
} /**
* 依据字符串的内容生成摘要
*
* @param val
* 字符串的内容
* @param charset
* 输入数据的编码方式
* @return 输出为一个long类型
*/
public static long createHash(String val, Charset charset) {
return createHash(val.getBytes(charset));
} /**
* 依据字符串内容生成摘要
*
* @param val
* 指定了输入的字符串。默认的编码为 UTF-8
* @return 输出为一个long类型
*/
public static long createHash(String val) {
return createHash(val, charset);
} /**
* 依据字节数组生成摘要
*
* @param data
* 输入数据
* @return 输出为long类型的摘要
*/
public static long createHash(byte[] data) {
long h = 0;
byte[] res; synchronized (digestFunction) {
res = digestFunction.digest(data);
} for (int i = 0; i < 4; i++) {
h <<= 8;
h |= ((int) res[i]) & 0xFF;
}
return h;
} /**
* 重写equals方法
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final BloomFilter<E> other = (BloomFilter<E>) obj;
if (this.expectedNumberOfFilterElements != other.expectedNumberOfFilterElements) {
return false;
}
if (this.k != other.k) {
return false;
}
if (this.bitSetSize != other.bitSetSize) {
return false;
}
if (this.bitset != other.bitset
&& (this.bitset == null || !this.bitset.equals(other.bitset))) {
return false;
}
return true;
} /**
* 重写了hashCode方法
*
*/
@Override
public int hashCode() {
int hash = 7;
hash = 61 * hash + (this.bitset != null ? this.bitset.hashCode() : 0);
hash = 61 * hash + this.expectedNumberOfFilterElements;
hash = 61 * hash + this.bitSetSize;
hash = 61 * hash + this.k;
return hash;
} /**
* 依据最大元素数量和过滤器的大小来计算误报率。 * 方法的返回值为误报率。假设插入的元素个数小于最大值,则误报率会比返回值要小。
*
* @return 期望的误报率.
*/
public double expectedFalsePositiveProbability() {
return getFalsePositiveProbability(expectedNumberOfFilterElements);
} /**
* 通过插入的元素数量和过滤器容器大小来计算实际的误报率。
*
* @param numberOfElements
* 插入的元素的个数.
* @return 误报率.
*/
public double getFalsePositiveProbability(double numberOfElements) {
// (1 - e^(-k * n / m)) ^ k
return Math.pow((1 - Math.exp(-k * (double) numberOfElements
/ (double) bitSetSize)), k); } /**
* 通过实际插入的元素数量和过滤器容器大小来计算实际的误报率。 *
* @return 误报率.
*/
public double getFalsePositiveProbability() {
return getFalsePositiveProbability(numberOfAddedElements);
} /**
* 返回哈希函数的个数 k
*
* @return k.
*/
public int getK() {
return k;
} /**
* 清空过滤器元素
*/
public void clear() {
bitset.clear();
numberOfAddedElements = 0;
} /**
* 向过滤器中加入元素。
* 加入的元素的toString()方法将会被调用。返回的字符串作为哈希函数的输出。 *
* @param element
* 要加入的元素
*/
public void add(E element) {
long hash;
String valString = element.toString();
for (int x = 0; x < k; x++) {
hash = createHash(valString + Integer.toString(x));
hash = hash % (long) bitSetSize;
bitset.set(Math.abs((int) hash), true);
}
numberOfAddedElements++;
} /**
* 加入一个元素集合到过滤器中
*
* @param c
* 元素集合.
*/
public void addAll(Collection<? extends E> c) {
for (E element : c)
add(element);
} /**
* 用来推断元素是否在过滤器中。 假设已存在。返回 true。
*
* @param element
* 要检查的元素.
* @return 假设预计该元素已存在。则返回true
*/
public boolean contains(E element) {
long hash;
String valString = element.toString();
for (int x = 0; x < k; x++) {
hash = createHash(valString + Integer.toString(x));
hash = hash % (long) bitSetSize;
if (!bitset.get(Math.abs((int) hash)))
return false;
}
return true;
} /**
* 推断一个集合中的元素是否都在过滤器中。 *
* @param c
* 要检查的元素集合
* @return 假设集合全部的元素都在过滤器中。则返回true。
*/
public boolean containsAll(Collection<? extends E> c) {
for (E element : c)
if (!contains(element))
return false;
return true;
} /**
* 得到某一位的值
*
* @param bit
* bit的位置.
* @return 假设该位被设置,则返回true。 */
public boolean getBit(int bit) {
return bitset.get(bit);
} /**
* 设置过滤器某一位的值
*
* @param bit
* 要设置的位置.
* @param value
* true表示已经成功设置。 false表示改为被清除。
*/
public void setBit(int bit, boolean value) {
bitset.set(bit, value);
} /**
* 返回存放信息的位数组.
*
* @return 位数组.
*/
public BitSet getBitSet() {
return bitset;
} /**
* 得到过滤器中位数组个大小。
*
* @return 数组大小.
*/
public int size() {
return this.bitSetSize;
} /**
* 返回已加入的元素的个数
*
* @return 元素个数.
*/
public int count() {
return this.numberOfAddedElements;
} /**
* 得到能加入的元素的最大数量
*
* @return 最大数量.
*/
public int getExpectedNumberOfElements() {
return expectedNumberOfFilterElements;
} /**
* 得到每一个元素占用的位的个数的期望值
*
* @return 每一个元素占用的位数
*/
public double getExpectedBitsPerElement() {
return this.bitsPerElement;
} /**
* 得到每一个元素占用位数的实际值
*
* @return 每一个元素占用的位数.
*/
public double getBitsPerElement() {
return this.bitSetSize / (double) numberOfAddedElements;
}
}
浅谈BloomFilter【下】用Java实现BloomFilter的更多相关文章
- [原创]浅谈NT下Ring3无驱进入Ring0的方法
原文链接:浅谈NT下Ring3无驱进入Ring0的方法 (测试环境:Windows 2000 SP4,Windows XP SP2.Windows 2003 未测试) 在NT下无驱进入Ring0是一个 ...
- 浅谈 IE下innerHTML导致的问题
原文:浅谈 IE下innerHTML导致的问题 先来看个demo吧: <!DOCTYPE html> <html> <head> <meta charset= ...
- []转帖] 浅谈Linux下的五种I/O模型
浅谈Linux下的五种I/O模型 https://www.cnblogs.com/chy2055/p/5220793.html 一.关于I/O模型的引出 我们都知道,为了OS的安全性等的考虑,进程是 ...
- 浅谈Vue下的components模板
浅谈Vue下的components模板在我们越来越深入Vue时,我们会发现我们对HTML代码的工程量会越来越少,今天我们来谈谈Vue下的 components模板的 初步使用方法与 应用 我们先来简单 ...
- 浅谈Linux下/etc/passwd文件
浅谈Linux 下/etc/passwd文件 看过了很多渗透测试的文章,发现在很多文章中都会有/etc/passwd这个文件,那么,这个文件中到底有些什么内容呢?下面我们来详细的介绍一下. 在Linu ...
- 浅谈Linux下如何修改IP
linux 下命令之浅谈//cd .. //返回上一级//创建文件夹touch test.txt//Linux不区分大小写//往一个文件中追加内容echo "****" > ...
- 浅谈《think in java》:一 对象导论总结
清单1. 抽象机制,面向对象程序设计方式 java所基于Smalltalk的特性表现一种纯粹的面向对象设计方式: 万物都是对象 程序是对象的集合(容器),他们通过发送消息(发送请求)来告知彼此所要做的 ...
- 浅谈 Objective-C 下对象的初始化
转自:http://www.oschina.net/question/54100_32468 众所周知,Objective-C是一门面向对象的语言,一般情况下,我们在Objective-C中定义一个类 ...
- 浅谈linux 下,利用Nginx服务器代理实现ajax跨域请求。
ajax跨域请求对于前端开发者几乎在任何一个项目中都会用到,众所周知,跨域请求有三种方式: jsonp; XHR2 代理: jsonp: 这种应该是开发中是使用的最多的,最常见的跨域请求方法,其实aj ...
- 浅谈Android下的Bitmap之大Bitmap加载
引言 我们常常提到的“Android程序优化”,通常指的是性能和内存的优化,即:更快的响应速度,更低的内存占用.Android程序的性能和内存问题,大部分都和图片紧密相关,而图片的加载在很多情况下很用 ...
随机推荐
- Day09文件操作
1.什么是文件 文件:操作系统为应用程序或者用户提供的一种操作硬盘的虚拟单位 强调:①文件是操作系统提供的虚拟单位②应用程序或者用户对文件的读写操作其实都是向操作系统发送指令 2.为什么要用文件 文件 ...
- PAT Basic 1051
1051 复数乘法 复数可以写成 (A+Bi) 的常规形式,其中 A 是实部,B 是虚部,i 是虚数单位,满足 i2=−1:也可以写成极坐标下的指数形式 (R×e(Pi)),其中 R 是复 ...
- MongoDB的正确使用姿势
本文来自网易云社区,转载务必请注明出处. MongoDB是一个非常有前途的数据库,MongoDB官方对自己的定位是通用数据库,其实这个定位跟MySQL有些像.虽其流行度还远未达到MySQL的水平,但笔 ...
- wlan
一.概述 CSMA/CD --->以太网介质 CSMA/CA------->无线介质 IEEE----->802.11 a b g e f h i j 分类 ...
- BNUOJ 5227 Max Sum
Max Sum Time Limit: 1000ms Memory Limit: 32768KB This problem will be judged on HDU. Original ID: ...
- oracle用户密码错误导致用户锁定
解决方法:使用DBA用户将其解锁: SQL> alter user ecology account unlock; 用户已更改. 用户密码限制设置: 查看FAILED_LOGIN_ATTEMPT ...
- xcode错误-第三方的东西他不支持
ld:' /用户/ tanqihong /桌面/金粒子公司/金粒子公司/ Carloans / Carloans /第三/ TongLianPay / lib_release / libAPayLib ...
- [POJ1143]Number Game
[POJ1143]Number Game 试题描述 Christine and Matt are playing an exciting game they just invented: the Nu ...
- BZOJ 2134 单选错位 ——期望DP
发现概率是∑1/两道题答案相同的概率, 稍加化简 #include <map> #include <ctime> #include <cmath> #include ...
- 刷题总结——小z的袜子(bzoj2038)
题目: Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……具体来说,小Z把 ...