本质上是一个很长的二进制向量和一系列随机映射函数
布隆过滤器可以用于检索一个元素是否在一个集合中
优点: 空间效率和查询时间都远远超过一般算法
缺点:
- 有一定的误识别率(判定不存在则一定不存在,判定存在则有可能存在)
- 删除困难
使用场景
商品页面:防止爬虫大量请求无效商品页面,流量打到数据库。缓存穿透
原理
当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们设置为1。检索时,只要看这些点是不是1就知道集合中有没有它了:
- 如果这些点有任何一个0,则被检索元素一定不存在
- 如果都是1,则被检索元素可能在。
package datastruct.bloomfilter;import java.util.BitSet;import java.util.Random;import java.util.Iterator;class BloomFilter implements Cloneable {private BitSet hashes;private RandomInRange prng;private int k; // Number of hash functionsprivate static final double LN2 = 0.6931471805599453; // ln(2)/*** Create a new bloom filter.** @param n Expected number of elements* @param m Desired size of the container in bits**/public BloomFilter(int n, int m) {k = (int) Math.round(LN2 * m / n);if (k <= 0) {k = 1;}this.hashes = new BitSet(m);this.prng = new RandomInRange(m, k);}/*** Create a bloom filter of 1Mib.** @param n Expected number of elements**/public BloomFilter(int n) {this(n, 1024 * 1024 * 8);}/*** Add an element to the container**/public void add(Object o) {prng.init(o);for (RandomInRange r : prng) {hashes.set(r.value);}}/*** If the element is in the container, returns true.* If the element is not in the container, returns true with a probability ≈ e^(-ln(2)² * m/n), otherwise false.* So, when m is large enough, the return value can be interpreted as:* - true : the element is probably in the container* - false : the element is definitely not in the container**/public boolean contains(Object o) {prng.init(o);for (RandomInRange r : prng) {if (!hashes.get(r.value)) {return false;}}return true;}/*** Removes all of the elements from this filter.**/public void clear() {hashes.clear();}/*** Create a copy of the current filter**/@Overridepublic BloomFilter clone() throws CloneNotSupportedException {return (BloomFilter) super.clone();}/*** Generate a unique hash representing the filter**/@Overridepublic int hashCode() {return hashes.hashCode() ^ k;}/*** Test if the filters have equal bitsets.* WARNING: two filters may contain the same elements, but not be equal* (if the filters have different size for example).*/public boolean equals(BloomFilter other) {return this.hashes.equals(other.hashes) && this.k == other.k;}/*** Merge another bloom filter into the current one.* After this operation, the current bloom filter contains all elements in* other.**/public void merge(BloomFilter other) {if (other.k != this.k || other.hashes.size() != this.hashes.size()) {throw new IllegalArgumentException("Incompatible bloom filters");}this.hashes.or(other.hashes);}private static class RandomInRangeimplements Iterable<RandomInRange>, Iterator<RandomInRange> {private Random prng;private int max; // Maximum value returned + 1private int count; // Number of random elements to generateprivate int i = 0; // Number of elements generatedpublic int value; // The current valueRandomInRange(int maximum, int k) {max = maximum;count = k;prng = new Random();}public void init(Object o) {prng.setSeed(o.hashCode());}public Iterator<RandomInRange> iterator() {i = 0;return this;}@Overridepublic RandomInRange next() {i++;value = prng.nextInt() % max;if (value < 0) {value = -value;}return this;}@Overridepublic boolean hasNext() {return i < count;}@Overridepublic void remove() {throw new UnsupportedOperationException();}}}
