java实现bloom filter_浅谈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指定了要插入过滤器中的元素的类型,eg,String Integer

* @author Magnus Skjegstad

* @translator xiaoye

*/

public class BloomFilter 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

* 指定了原来过滤器的数据的数量

* filterData 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 other = (BloomFilter) 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;

}

}

原文:http://blog.csdn.net/zq602316498/article/details/40660695

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值