通过前一篇文章的学习,对于 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的更多相关文章

  1. [原创]浅谈NT下Ring3无驱进入Ring0的方法

    原文链接:浅谈NT下Ring3无驱进入Ring0的方法 (测试环境:Windows 2000 SP4,Windows XP SP2.Windows 2003 未测试) 在NT下无驱进入Ring0是一个 ...

  2. 浅谈 IE下innerHTML导致的问题

    原文:浅谈 IE下innerHTML导致的问题 先来看个demo吧: <!DOCTYPE html> <html> <head> <meta charset= ...

  3. []转帖] 浅谈Linux下的五种I/O模型

    浅谈Linux下的五种I/O模型 https://www.cnblogs.com/chy2055/p/5220793.html  一.关于I/O模型的引出 我们都知道,为了OS的安全性等的考虑,进程是 ...

  4. 浅谈Vue下的components模板

    浅谈Vue下的components模板在我们越来越深入Vue时,我们会发现我们对HTML代码的工程量会越来越少,今天我们来谈谈Vue下的 components模板的 初步使用方法与 应用 我们先来简单 ...

  5. 浅谈Linux下/etc/passwd文件

    浅谈Linux 下/etc/passwd文件 看过了很多渗透测试的文章,发现在很多文章中都会有/etc/passwd这个文件,那么,这个文件中到底有些什么内容呢?下面我们来详细的介绍一下. 在Linu ...

  6. 浅谈Linux下如何修改IP

    linux 下命令之浅谈//cd ..  //返回上一级//创建文件夹touch test.txt//Linux不区分大小写//往一个文件中追加内容echo "****" > ...

  7. 浅谈《think in java》:一 对象导论总结

    清单1. 抽象机制,面向对象程序设计方式 java所基于Smalltalk的特性表现一种纯粹的面向对象设计方式: 万物都是对象 程序是对象的集合(容器),他们通过发送消息(发送请求)来告知彼此所要做的 ...

  8. 浅谈 Objective-C 下对象的初始化

    转自:http://www.oschina.net/question/54100_32468 众所周知,Objective-C是一门面向对象的语言,一般情况下,我们在Objective-C中定义一个类 ...

  9. 浅谈linux 下,利用Nginx服务器代理实现ajax跨域请求。

    ajax跨域请求对于前端开发者几乎在任何一个项目中都会用到,众所周知,跨域请求有三种方式: jsonp; XHR2 代理: jsonp: 这种应该是开发中是使用的最多的,最常见的跨域请求方法,其实aj ...

  10. 浅谈Android下的Bitmap之大Bitmap加载

    引言 我们常常提到的“Android程序优化”,通常指的是性能和内存的优化,即:更快的响应速度,更低的内存占用.Android程序的性能和内存问题,大部分都和图片紧密相关,而图片的加载在很多情况下很用 ...

随机推荐

  1. 杭电 1051 Wooden Sticks

    Description There is a pile of n wooden sticks. The length and weight of each stick are known in adv ...

  2. LeetCode 673. Number of Longest Increasing Subsequence

    Given an unsorted array of integers, find the number of longest increasing subsequence. Example 1: I ...

  3. 解决like '%字符串%'时索引不被使用的方法

    解决like '%字符串%'时索引不被使用的方法   分步阅读 解决like '%字符串%'时索引不被使用的方法,如果like以通配符开头('%abc')时索引会失效会变成全表扫描的操作. 工具/原料 ...

  4. 算法学习记录-图——应用之拓扑排序(Topological Sort)

    这一篇写有向无环图及其它的应用: 清楚概念: 有向无环图(DAG):一个无环的有向图.通俗的讲就是从一个点沿着有向边出发,无论怎么遍历都不会回到出发点上. 有向无环图是描述一项工程或者系统的进行过程的 ...

  5. Java-从字符串或一个子字符串中搜索一个字符

    indexOf函数 package com.tj; public class MyClass implements Cloneable { public static void main(String ...

  6. angularJs 中ui-router 路由向controller传递数据

    页面上 : ui-sref="home.dataAnalysis({role:'thirdpart:tokenverify',menuType:'a'})" 路由设置 .state ...

  7. map 插入数据的方式局别

    #include<map> #include<iostream> usingnamespace std; int main() { map <int, int> m ...

  8. CSU-1336: Interesting Calculator,最短路思想!

    1336: Interesting Calculator 这道题被LZQ抢了一血,于是去读题发现题意不难,纯广搜结果写的一塌糊涂. 题意:给你两个数x,y.由x每次可以经过一系列操作变换,每个变换都有 ...

  9. mac安装R语言启动后-Warning messages

    During startup - Warning messages: : Setting LC_CTYPE failed, using "C" : Setting LC_COLLA ...

  10. 【Luogu】P3758可乐(矩阵优化DP)

    题目链接 一开始想到这可能能用矩阵优化,但以为暴力就能卡过……T成二十分 首先我们回顾一下我们的暴力转移方程 用f[i][j][0/1]表示在i时刻,j点,1不爆炸,0已爆炸的方案数,那么f[i][j ...