本质上是一个很长的二进制向量和一系列随机映射函数
布隆过滤器可以用于检索一个元素是否在一个集合中
优点: 空间效率和查询时间都远远超过一般算法

缺点:

  • 有一定的误识别率(判定不存在则一定不存在,判定存在则有可能存在
  • 删除困难

使用场景

商品页面:防止爬虫大量请求无效商品页面,流量打到数据库。缓存穿透

image.png

原理

当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们设置为1。检索时,只要看这些点是不是1就知道集合中有没有它了:

  1. 如果这些点有任何一个0,则被检索元素一定不存在
  2. 如果都是1,则被检索元素可能在。
  1. package datastruct.bloomfilter;
  2. import java.util.BitSet;
  3. import java.util.Random;
  4. import java.util.Iterator;
  5. class BloomFilter implements Cloneable {
  6. private BitSet hashes;
  7. private RandomInRange prng;
  8. private int k; // Number of hash functions
  9. private static final double LN2 = 0.6931471805599453; // ln(2)
  10. /**
  11. * Create a new bloom filter.
  12. *
  13. * @param n Expected number of elements
  14. * @param m Desired size of the container in bits
  15. **/
  16. public BloomFilter(int n, int m) {
  17. k = (int) Math.round(LN2 * m / n);
  18. if (k <= 0) {
  19. k = 1;
  20. }
  21. this.hashes = new BitSet(m);
  22. this.prng = new RandomInRange(m, k);
  23. }
  24. /**
  25. * Create a bloom filter of 1Mib.
  26. *
  27. * @param n Expected number of elements
  28. **/
  29. public BloomFilter(int n) {
  30. this(n, 1024 * 1024 * 8);
  31. }
  32. /**
  33. * Add an element to the container
  34. **/
  35. public void add(Object o) {
  36. prng.init(o);
  37. for (RandomInRange r : prng) {
  38. hashes.set(r.value);
  39. }
  40. }
  41. /**
  42. * If the element is in the container, returns true.
  43. * If the element is not in the container, returns true with a probability ≈ e^(-ln(2)² * m/n), otherwise false.
  44. * So, when m is large enough, the return value can be interpreted as:
  45. * - true : the element is probably in the container
  46. * - false : the element is definitely not in the container
  47. **/
  48. public boolean contains(Object o) {
  49. prng.init(o);
  50. for (RandomInRange r : prng) {
  51. if (!hashes.get(r.value)) {
  52. return false;
  53. }
  54. }
  55. return true;
  56. }
  57. /**
  58. * Removes all of the elements from this filter.
  59. **/
  60. public void clear() {
  61. hashes.clear();
  62. }
  63. /**
  64. * Create a copy of the current filter
  65. **/
  66. @Override
  67. public BloomFilter clone() throws CloneNotSupportedException {
  68. return (BloomFilter) super.clone();
  69. }
  70. /**
  71. * Generate a unique hash representing the filter
  72. **/
  73. @Override
  74. public int hashCode() {
  75. return hashes.hashCode() ^ k;
  76. }
  77. /**
  78. * Test if the filters have equal bitsets.
  79. * WARNING: two filters may contain the same elements, but not be equal
  80. * (if the filters have different size for example).
  81. */
  82. public boolean equals(BloomFilter other) {
  83. return this.hashes.equals(other.hashes) && this.k == other.k;
  84. }
  85. /**
  86. * Merge another bloom filter into the current one.
  87. * After this operation, the current bloom filter contains all elements in
  88. * other.
  89. **/
  90. public void merge(BloomFilter other) {
  91. if (other.k != this.k || other.hashes.size() != this.hashes.size()) {
  92. throw new IllegalArgumentException("Incompatible bloom filters");
  93. }
  94. this.hashes.or(other.hashes);
  95. }
  96. private static class RandomInRange
  97. implements Iterable<RandomInRange>, Iterator<RandomInRange> {
  98. private Random prng;
  99. private int max; // Maximum value returned + 1
  100. private int count; // Number of random elements to generate
  101. private int i = 0; // Number of elements generated
  102. public int value; // The current value
  103. RandomInRange(int maximum, int k) {
  104. max = maximum;
  105. count = k;
  106. prng = new Random();
  107. }
  108. public void init(Object o) {
  109. prng.setSeed(o.hashCode());
  110. }
  111. public Iterator<RandomInRange> iterator() {
  112. i = 0;
  113. return this;
  114. }
  115. @Override
  116. public RandomInRange next() {
  117. i++;
  118. value = prng.nextInt() % max;
  119. if (value < 0) {
  120. value = -value;
  121. }
  122. return this;
  123. }
  124. @Override
  125. public boolean hasNext() {
  126. return i < count;
  127. }
  128. @Override
  129. public void remove() {
  130. throw new UnsupportedOperationException();
  131. }
  132. }
  133. }