通过前一篇文章的学习,对于 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;
}
}

最新文章

  1. Unity3D 更新文件下载器
  2. Shell 编程基础之 [ 与 [[ 的异同
  3. include(thinkphp常用内置标签)
  4. 如何下载spring-framework
  5. Struct2 向Action中传递参数(中文乱码问题)
  6. python基础之 optparse.OptionParser
  7. InnoDB的配置
  8. C# 它 抽象类和接口
  9. dns是什么
  10. unit正交相机Size的计算公式
  11. gulp学习指南之CSS合并、压缩与MD5命名及路径替换
  12. System.map详解
  13. AutoMapper IIS回收引发的 未将对象引用设置到对象实例
  14. Tomcat系列(4)——Tomcat 组件及架构详细部分
  15. mybatis常用类起别名
  16. sublime-text3按tab跳出括号
  17. mac navicate 2013 - Lost connection to MySQL server at &#39;reading initial communication packet
  18. flex布局中transform出错
  19. greys java在线诊断工具
  20. java 自定义异常输出信息(使用构造器)

热门文章

  1. VS做简历的第三天(将文件中的样式保存并且导入)
  2. 删除mysql主从
  3. I2C驱动框架(五)
  4. Ubuntu 14.04在虚拟机上的桥接模式下设置静态IP
  5. go 本地安装 grpc-go
  6. Delphi第三方控件安装卸载指南
  7. 新技术---- MongoDB
  8. 【LeetCode】Reverse Nodes in k-Group(k个一组翻转链表)
  9. Terracotta服务器的不同配置方式
  10. iOS学习笔记14-网络(三)WebView