场景题:假设有40亿QQ号,但只有1G内存,如何实现去重?

当数据量比较大时,使用常规的方式来判重就不行了。例如,使用 MySQL 数据库判重,或使用 List.contains() 或 Set.contains() 判重就不行了,因为数据量太大会导致内存放不下,或查询速度太慢等问题。

1.空间占用量预测

正常情况下,如果将 40 亿 QQ 号存储在 Java 中的 int 类型的话,一个 int 占 4 字节(byte)那么 40 亿占用空间大小为:

4000000000*4/1024/1024/1024=14.9 GB

1GB=1024MB,1MB=1024KB,1KB=1024B(byte)

所以,我们无法使用正常的手段进行 40 亿 QQ 号的存储和去重判断,那怎么实现呢?

2.解决方案

此问题的常见解决方案有两种:

  1. 使用位数组 BitMap 实现判重。
  2. 使用布隆过滤器实现判重。

具体来说。

2.1 位数组实现判重

位数组是指使用位(bit)组成的数组,每个 QQ 号使用 1 位(bit)来存储,如下图所示:

alt

其中下标用来标识具体的数字,例如以上图片标识 1、3 数字存在,如果值为 0 表示不存在,这样的话 40 亿占用的位数组空间位 40 亿 bit,也就是 4000000000/1024/1024/1024/8=0.465 GB,不到 1G 的内存就可以存储 40 亿 QQ 号了,查询某个 QQ 号是否在线,只需要看这个 QQ 下标对应的位置是否为 1,1 表示存在,0 表示不存在。

位数组代码实现

位数组可以使用 Java 自带的 BitSet 来实现,它位于 java.util 包中,具体实现代码如下:

import java.util.BitSet;

public class BitmapExample {
    public static void main(String[] args) {
        // 创建一个BitSet实例
        BitSet bitmap = new BitSet();

        // 设置第5个位置为1,表示第5个元素存在
        bitmap.set(5);

        // 检查第5个位置是否已设置
        boolean exists = bitmap.get(5);
        System.out.println("Element exists: " + exists);  // 输出: Element exists: true

        // 设置从索引10到20的所有位置为1
        bitmap.set(10, 21);  // 参数是包含起始点和不包含终点的区间

        // 计算bitset中所有值为1的位的数量,相当于计算设置了的元素个数
        int count = bitmap.cardinality();
        System.out.println("Number of set bits: " + count);

        // 清除第5个位置
        bitmap.clear(5);

        // 判断位图是否为空
        boolean isEmpty = bitmap.isEmpty();
        System.out.println("Is the bitset empty? " + isEmpty);
    }
}

2.2 布隆过器实现

布隆过滤器是基于位数组实现的,它是一种高效的数据结构,由布隆在 1970 年提出。它主要用于判断一个元素可能是否存在于集合中,其核心特性包括高效的插入和查询操作,但**存在一定的假阳性(False Positives)**可能性。

布隆过滤器实现如下图所示:

alt

根据 key 值计算出它的存储位置,然后将此位置标识全部标识为 1(未存放数据的位置全部为 0),查询时也是查询对应的位置是否全部为 1,如果全部为 1,则说明数据是可能存在的,否则一定不存在

布隆过器特性:如果布隆过滤器说一个元素不在集合中,那么它一定不在这个集合中;但如果它说一个元素在集合中,则有可能是不存在的(存在误差,假阳性)。

布隆过器代码实现

布隆过滤器的常见实现有以下几种方式:

  1. 使用 Google Guava BloomFilter 实现布隆过滤器,具体实现代码如下:
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
    }
}
  1. 使用 Hutool 框架 BitMapBloomFilter 实现布隆过滤器,如下代码所示:
// 初始化
BitMapBloomFilter filter = new BitMapBloomFilter(10);
// 存放数据
filter.add("123");
filter.add("abc");
filter.add("ddd");
// 查找
filter.contains("abc");
  1. 使用 Redisson 框架中的 RBloomFilter 实现布隆过滤器,如下代码所示:
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redissonClient = Redisson.create(config);
// 创建布隆过滤器,设置名称和期望容量与误报率
RBloomFilter<String> bloomFilter = 
redissonClient.getBloomFilter("myBloomFilter");
bloomFilter.tryInit(10000, 0.03); // 期望容量 10000,误报率 3%
// 添加元素到布隆过滤器
String element1 = "element1";
bloomFilter.add(element1);
// 判断元素是否存在
boolean mightExist = bloomFilter.contains(element1);
System.out.println("元素 " + element1 + " 可能存在: " + mightExist);
String element2 = "element2";
boolean mightExist2 = bloomFilter.contains(element2);
System.out.println("元素 " + element2 + " 可能存在: " + mightExist2);

其中 Google Guava BloomFilter 和 Hutool 框架 BitMapBloomFilter 为单机版的布隆过滤器实现,不适用分布式环境。分布式环境要使用 Redisson 框架中的 RBloomFilter 来实现布隆过滤器,因为它的数据是保存在 Redis 中间件的,而中间件天生支持分布式系统。

小结

位数组和布隆过滤器的区别如下:

  • 位数组:没有误判,但空间利用率低。
  • 布隆过滤器:空间利用率高,但存在对已经存在的数据的误判(不存在的数据没有误判)。

因此,如果对精准度要求高可以使用位数组;如果对空间要求苛刻,可以考虑布隆过滤器。

#场景题#
全部评论
点赞 回复 分享
发布于 今天 17:08 北京

相关推荐

昨天 16:07
中南大学 Java
黑皮白袜臭脚体育生:简历准备好了的话建议现在就开始投,去了后狠狠偷组里技术文档包装成自己的,不理解的地方旁敲侧击做这块的同事和gpt辅助问清楚,再把实习时长写到11月或12月开始,这样日常实习就有含金量了,如果没找到的话本9或硕9直接冲暑期也没什么问题
点赞 评论 收藏
分享
中上985本&nbsp;软件工程&nbsp;练习Java将近两年半暑假开始背八股,小林coding三刷(os只看了两遍),JavaGuide大部分三刷,面试鸭都过了一遍,lc&nbsp;&nbsp;200+。11.1开始大面积投大厂(除了字节美团)结果将近一周都没有回应,后来才知道官网投递是效率最低的方式。过一周后开始在ssob上大面积投。11.8&nbsp;美国道富&nbsp;一面&nbsp;ssob&nbsp;oc拒11.11&nbsp;快手电商&nbsp;一面&nbsp;官网&nbsp;挂&nbsp;因为是第一次大厂面试&nbsp;有点紧张而且部分八股没覆盖好&nbsp;手撕是岛屿问题和判断两个二叉树相等11.12&nbsp;阿里健康-小鹿中医&nbsp;一面过&nbsp;ssob11.12&nbsp;高德&nbsp;一面挂&nbsp;ssob&nbsp;八股比较基础&nbsp;都打上来了&nbsp;但是手撕很简单的没撕出来&nbsp;所以挂了&nbsp;比较可惜11.14&nbsp;喜马拉雅&nbsp;一面挂&nbsp;ssob&nbsp;八股答的好&nbsp;但是手撕没写出来&nbsp;是买卖股票问题的一道题&nbsp;两次买卖的那道&nbsp;当时没刷到所以没写出来&nbsp;11.14&nbsp;阿里健康-小鹿中医&nbsp;二面后无后续11.21&nbsp;快手商业化&nbsp;一面过&nbsp;官网捞起来的&nbsp;八股打得很好&nbsp;算法卡了一下&nbsp;但是也写出来了11.28&nbsp;快手商业化&nbsp;二面挂&nbsp;无八股&nbsp;一些小场景&nbsp;手撕快排&nbsp;写出来了但是线上编译环境一致输出不正确&nbsp;二面挂(面试官看着不太正经&nbsp;体验较差)12.4&nbsp;字节国际化短视频直播&nbsp;一面过&nbsp;官网&nbsp;结合项目问场景和八股&nbsp;手撕是岛屿问题12.9&nbsp;字节国际化短视频直播&nbsp;二面过&nbsp;一直在问场景&nbsp;有点被拷打&nbsp;手撕是二叉树层序遍历&nbsp;最后也是过了12.16&nbsp;字节三面&nbsp;一直疯狂场景:亿级点赞系统设计&nbsp;缓存设计&nbsp;冷热数据&nbsp;手撕是之前快手一面手撕原题&nbsp;秒撕&nbsp;最后还是挂了&nbsp;应该是场景答的一般&nbsp;内心比较崩溃#Java#12.20&nbsp;Momenta&nbsp;数仓&nbsp;一面&nbsp;内推&nbsp;纯做题&nbsp;三个sql&nbsp;两个手撕&nbsp;最后挂了&nbsp;可能是手撕没撕好12.23&nbsp;字节复活后一面&nbsp;无八股&nbsp;sql题+智力题+中序和前序遍历构建二叉树&nbsp;sql很简单但是卡了一下不太应该&nbsp;智力题没整明白&nbsp;第二天感谢信12.25&nbsp;中科软&nbsp;一面oc&nbsp;后来才知道是个大外包.....12.26&nbsp;唯品会一面&nbsp;ssob&nbsp;手撕+八股+场景&nbsp;手撕没ac但是也过了12.26&nbsp;华为一面&nbsp;官网&nbsp;八股+手撕都挺好&nbsp;面试后5min通过12.27&nbsp;杭州每刻&nbsp;一面oc12.28&nbsp;京东一面过&nbsp;四十分钟八股盛宴&nbsp;发挥比较完美12.28&nbsp;华为leader面&nbsp;沟通过程中ld说部门不是搞Java的&nbsp;所以当场就说没意向去了&nbsp;故挂1.2&nbsp;车来了&nbsp;ssob&nbsp;一面oc1.3&nbsp;京东hr面&nbsp;当晚电话oc1.7&nbsp;京东offer&nbsp;1.7&nbsp;唯品会&nbsp;二面拒总结下这两个多月:boss上投了有500以上&nbsp;官网上投递了20+大厂&nbsp;其中boss上都是些小公司,官网约面的只有字节、快手、京东,大厂面试寥寥无几(可能是本人没有好实习经历的原因),其中快手二面挂、字节三面挂都蛮搞心态的。甚至深深陷入了自我怀疑,经常半夜睡不着觉。但最后的京东也是靠扎实的八股拿下了,也是将自己从无尽的焦虑中解救了出来。对于日常实习个人摸索出了一些经验:八股大部分都比较常规&nbsp;不会有特别深入底层的&nbsp;基本都是八股网站上比较重点的(小林coding+javaguide吃透就足够)&nbsp;对于算法(手撕)一定要准备好再去面试!!!因为就算八股答的很好&nbsp;手撕写不出来也会寄&nbsp;本人高德&nbsp;喜马拉雅就是因为lc当时没刷好挂掉的&nbsp;至少也要lc&nbsp;200道(代码sxl+hot&nbsp;100)熟了以后再面&nbsp;的话&nbsp;90%的概率可以写得出来。现在开始冲完全来得及暑期实习。而且最重要的一点!!!&nbsp;不要对大厂怯魅,因为大厂问的东西也很正常&nbsp;放松心态&nbsp;相信自己的积累也可以拿下还有一点&nbsp;日常实习真的运气占50%以上&nbsp;有的岗位你来的早就是你的&nbsp;而且有的面试比较水而且只有一面&nbsp;说不定运气好就可以直接oc,要相信自己的努力总会有结果的
数学转码崽:请问算法把代码随想录刷完了,lc上面得刷多少能对付大厂算法题呢
查看6道真题和解析
点赞 评论 收藏
分享
评论
3
16
分享
牛客网
牛客企业服务