判断元素在亿级数据中是否存在-布隆过滤算法

721 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情

题目:

现在有一个非常庞大的数据,假设全是 int 类型。现在我给你一个数,你需要告诉我它是否存在其中(尽量高效)。

解答:

  1. 如果使用 map 放入然后判断,这样如果数据量大了就会内存溢出。
  2. 基于上面分析的条件,要实现这个需求最需要解决的是如何将庞大的数据 load 到内存中。

而我们是否可以换种思路,因为只是需要判断数据是否存在,也不是需要把数据查询出来,所以完全没有必要将真正的数据存放进去。BurtonHowardBloom 在 1970 年提出了一个叫做 BloomFilter(中文翻译:布隆过滤)的算法。

BloomFilter 算法

BloomFilter:它是一个保存了很长的二级制向量,同时结合 Hash 函数实现的。

如图所示:

  • 首先需要初始化一个二进制的数组,长度设为 L(图中为 8),同时初始值全为 0 。
  • 当写入一个 A1=1000 的数据时,需要进行 H 次 hash 函数的运算(这里为 2 次);与 HashMap 有点类似,通过算出的 HashCode 与 L 取模后定位到 0、2 处,将该处的值设为 1。
  • A2=2000 也是同理计算后将 4、7 位置设为 1。
  • 当有一个 B1=1000 需要判断是否存在时,也是做两次 Hash 运算,定位到 0、2 处,此时他们的值都为 1 ,所以认为 B1=1000 存在于集合中。
  • 当有一个 B2=3000 时,也是同理。第一次 Hash 定位到 index=4 时,数组中的值为 1,所以再进行第二次 Hash 运算,结果定位到 index=5 的值为 0,所以认为 B2=3000 不存在于集合中。

所以布隆过滤有以下几个特点:

  • 只要返回数据不存在,则肯定不存在。
  • 返回数据存在,但只能是大概率存在。
  • 同时不能清除其中的数据。

在 Google Guava 库中实现了该算法。使用示例:

@Test
public void guavaTest() {
        long star = System.currentTimeMillis();
       BloomFilter<Integer> filter = BloomFilter.create(
               Funnels.integerFunnel(),
              10000000,
               0.01);

        for (int i = 0; i < 10000000; i++) {
             filter.put(i);
         }

         Assert.assertTrue(filter.mightContain(1));
         Assert.assertTrue(filter.mightContain(2));
         Assert.assertTrue(filter.mightContain(3));
         Assert.assertFalse(filter.mightContain(10000000));
         long end = System.currentTimeMillis();
         System.out.println("执行时间:" + (end - star));
     }