场景题:海量数据如何判重?

在海量数据如何确定一个值是否存在?这是一道非常经典的面试场景题。

那怎么回答这个问题呢?接下来咱们就详细的聊一聊。

参考答案

判断一个值是否存在?通常有以下两种解决方案:

  1. 使用哈希表:可以将数据进行哈希操作,将数据存储在相应的桶中。查询时,根据哈希值定位到对应的桶,然后在桶内进行查找。这种方法的时间复杂度为 O(1),但需要额外的存储空间来存储哈希表。如果桶中存在数据,则说明此值已存在,否则说明未存在。
  2. 使用布隆过滤器:布隆过滤器是一种概率型数据结构,用于判断一个元素是否在集合中。它利用多个哈希函数映射数据到一个位数组,并将对应位置置为 1。查询时,只需要对待查询的数据进行哈希,并判断对应的位是否都为 1。如果都为 1,则该数据可能存在;如果有一个位不为 1,则该数据一定不存在。布隆过滤器的查询时间复杂度为 O(k),其中 k 为哈希函数的个数。

相同点和不同点

它们两的相同点是:它们都存在误判的情况。例如,使用哈希表时,不同元素的哈希值可能相同,所以这样就产生误判了;而布隆过滤器的特征是,当布隆过滤器说,某个数据存在时,这个数据可能不存在;当布隆过滤器说,某个数据不存在时,那么这个数据一定不存在。

它们两的区别主要有以下几点:

  1. 存储机制:哈希表使用一个数组来存储键值对,通过哈希函数将键映射到数组的索引位置,然后将值存储在对应的位置上。而布隆过滤器则使用一个位数组(或位向量),通过多个哈希函数将元素映射到位数组的多个位上。
  2. 查询操作:哈希表在进行查询时,通过计算哈希值来定位键值对的存储位置,然后直接获取对应的值。查询时间复杂度通常为 O(1)。布隆过滤器在进行查询时,也通过多个哈希函数计算多个位,然后判断对应的位是否都为 1 来确定元素是否存在。查询时间复杂度为 O(k),其中 k 为哈希函数的个数。
  3. 内存占用:哈希表需要根据数据规模来动态调整数组的大小,以保证存储效率。而布隆过滤器在预先设置位数组的大小后,不会随数据规模的增加而增长。因此布隆过滤器更适用于海量数据

结论

哈希表和布隆过滤器都能实现判重,但它们都会存在误判的情况,但布隆过滤器存储占用的空间更小,更适合海量数据的判重。

布隆过滤器实现原理

布隆过滤器的实现,主要依靠的是它数据结构中的一个位数组,每次存储键值的时候,不是直接把数据存储在数据结构中,因为这样太占空间了,它是利用几个不同的无偏哈希函数,把此元素的 hash 值均匀的存储在位数组中,也就是说,每次添加时会通过几个无偏哈希函数算出它的位置,把这些位置设置成 1 就完成了添加操作。

当进行元素判断时,查询此元素的几个哈希位置上的值是否为 1,如果全部为 1,则表示此值存在,如果有一个值为 0,则表示不存在。因为此位置是通过 hash 计算得来的,所以即使这个位置是 1,并不能确定是那个元素把它标识为 1 的,因此布隆过滤器查询此值存在时,此值不一定存在,但查询此值不存在时,此值一定不存在

并且当位数组存储值比较稀疏的时候,查询的准确率越高,而当位数组存储的值越来越多时,误差也会增大。

位数组和 key 之间的关系,如下图所示: image.png

如何实现布隆过滤器?

布隆过滤器的实现通常有以下两种方案:

  1. 通过程序实现(内存级别方案):使用 Google Guava 库和 Apache Commons 库实现布隆过滤器。
  2. 通过中间件实现(支持数据持久化):使用 Redis 4.0 之后提供的布隆过滤插件来实现,它的好处是支持持久化,数据不会丢失。

Guava 实现布隆过滤器

使用 Google Guava 库实现布隆过滤器总共分为以下两步:

  1. 引入 Guava 依赖
  2. 使用 Guava API 操作布隆过滤器

具体实现如下。

① 引入 Guava 依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
</dependency>

② 使用 Guava API

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

public class BloomFilterExample {
    public static void main(String[] args) {
        // 创建一个布隆过滤器,设置期望插入的数据量为10000,期望的误判率为0.01
        BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.unencodedCharsFunnel(), 10000, 0.01);

        // 向布隆过滤器中插入数据
        bloomFilter.put("data1");
        bloomFilter.put("data2");
        bloomFilter.put("data3");

        // 查询元素是否存在于布隆过滤器中
        System.out.println(bloomFilter.mightContain("data1")); // true
        System.out.println(bloomFilter.mightContain("data4")); // false
    }
}

在上述示例中,我们通过 BloomFilter.create() 方法创建一个布隆过滤器,指定了元素序列化方式、期望插入的数据量和期望的误判率。然后,我们可以使用 put() 方法向布隆过滤器中插入数据,使用 mightContain() 方法来判断元素是否存在于布隆过滤器中。

小结

在海量数据如何确定一个值是否存在?通常有两种解决方案:哈希表和布隆过滤器,而它们两都存在误判的情况,但布隆过滤器更适合海量数据的判断,因为它占用的数据空间更小。布隆过滤器的特征是:当布隆过滤器说,某个数据存在时,这个数据可能不存在;当布隆过滤器说,某个数据不存在时,那么这个数据一定不存在。

#java面试题#
全部评论

相关推荐

03-30 20:12
已编辑
东南大学 C++
1.你做一下自我介绍。2.你的规划是什么?3.你说的是长期规划,那技术方向上有什么倾向?4.你有考研的计划吗?5.你是打算本科毕业直接工作吗?6.你们现在保研结果出来了吗?7.你现在有没有其他实习&nbsp;offer?8.字节那边的实习没有继续做了吗?9.你现在不能再回原来的组实习了吗?10.如果原来实习的组给你&nbsp;offer,你会去吗?11.你做的&nbsp;C++&nbsp;客户端是&nbsp;PC&nbsp;端的吗?12.你们做的是一个&nbsp;C++&nbsp;SDK,对接&nbsp;iOS、安卓和鸿蒙,是吗?题目一&nbsp;/&nbsp;C++&nbsp;并发相关15.这段代码有什么问题?16.为什么这里会出现多线程问题?17.除了加锁,还有什么解决方法?18.你先解释一下&nbsp;static&nbsp;作用在变量上、作用在函数上有什么区别。19.如果这个函数是类里的普通成员函数,里面定义了一个&nbsp;static&nbsp;变量,再对它做&nbsp;push_back,会有什么问题?20.为什么这个&nbsp;static&nbsp;变量不会每次调用都重新初始化?21.如果你用锁来解决,这个锁应该声明在哪里?22.如果这个类实例化出两个对象&nbsp;A&nbsp;和&nbsp;B,它们都会调这个函数,那是不是也会操作同一个数组?23.那这里是不是应该用静态锁,或者类似的全局共享锁?24.除了用锁之外,还有没有别的解决办法?25.你提到原子变量,那你怎么理解原子变量?26.如果代码可以随便改,为什么不能把&nbsp;static&nbsp;去掉?27.把&nbsp;static&nbsp;去掉以后,能不能解决并发问题?28.如果把&nbsp;static&nbsp;去掉,会引入什么额外问题?C++&nbsp;/&nbsp;容器&nbsp;/&nbsp;数据结构29.你简历里提到用了&nbsp;concurrent&nbsp;hashmap,可以介绍一下吗?30.哈希表的实现原理你知道吗?31.哈希表是有序的还是无序的?32.如果要把它做成“按插入顺序有序”,你会怎么做?33.标准库里的&nbsp;std::map&nbsp;底层实现原理你了解吗?实习项目追问34.你介绍一下你说的这个三档&nbsp;TTL&nbsp;和分层缓存框架。35.这个&nbsp;key&nbsp;是怎么设计的?36.value&nbsp;里存的是什么?37.这个缓存是怎么更新的?怎么触发更新?38.这样的话使用方如果拿到的是过期值,是不是要多等一次回源时间?39.你们的缓存命中率大概是多少?40.你拿到这个&nbsp;key&nbsp;之后,怎么知道去访问哪一档缓存?41.这个分档是动态判断的,还是你们手动维护配置文件?题目二&nbsp;/&nbsp;设计与代码实现操作系统52.你对虚拟内存有什么了解?53.虚拟内存是怎么实现的?54.系统怎么找到被换到磁盘上的那块数据?55.这个地址映射是怎么设计的?56.这个映射关系的数据结构叫什么名字?SQL&nbsp;优化&nbsp;/&nbsp;实习项目57.你之前做过&nbsp;SQL&nbsp;优化,是吧?58.解释一下什么是&nbsp;SQL&nbsp;签名化。59.这个服务是在后端部署的服务查数据库,还是端上的&nbsp;SDK&nbsp;查本地数据库?60.你查端上的数据库时,每次都要建立连接吗?61.你说多个&nbsp;SQL&nbsp;合并之后,只查最小时间戳,是什么意思?62.你们做这个优化的前提,是多个请求同时发过来,对吧?63.你们会去缓存这些请求吗?64.这个缓存多久?题目一二详见图片,正常面试一直在追问,然后不给反馈,全程冷脸。当然也认识到很多知识盲区了。很多不重要的问题删了,大多数是问实习。
点赞 评论 收藏
分享
评论
2
26
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务