Repository navigation
Expand file tree
/
Copy pathBloomFilter.java
More file actions
378 lines (345 loc) · 11 KB
/
Copy pathBloomFilter.java
File metadata and controls
378 lines (345 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
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
* 原版:https://github.com/MagnusS/Java-BloomFilter/blob/master/src/com/skjegstad/utils/BloomFilter.java
*/
public class BloomFilter<E> implements Serializable {
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;
}
}