场景题:假设有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.解决方案
此问题的常见解决方案有两种:
- 使用位数组 BitMap 实现判重。
- 使用布隆过滤器实现判重。
具体来说。
2.1 位数组实现判重
位数组是指使用位(bit)组成的数组,每个 QQ 号使用 1 位(bit)来存储,如下图所示:
其中下标用来标识具体的数字,例如以上图片标识 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)可能性。
布隆过滤器实现如下图所示:

根据 key 值计算出它的存储位置,然后将此位置标识全部标识为 1(未存放数据的位置全部为 0),查询时也是查询对应的位置是否全部为 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
}
}
- 使用 Hutool 框架 BitMapBloomFilter 实现布隆过滤器,如下代码所示:
// 初始化
BitMapBloomFilter filter = new BitMapBloomFilter(10);
// 存放数据
filter.add("123");
filter.add("abc");
filter.add("ddd");
// 查找
filter.contains("abc");
- 使用 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 中间件的,而中间件天生支持分布式系统。
小结
位数组和布隆过滤器的区别如下:
- 位数组:没有误判,但空间利用率低。
- 布隆过滤器:空间利用率高,但存在对已经存在的数据的误判(不存在的数据没有误判)。
因此,如果对精准度要求高可以使用位数组;如果对空间要求苛刻,可以考虑布隆过滤器。
本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:场景题、并发编程、MySQL、Redis、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、JVM、设计模式、消息队列等模块。
场景题:假设有40亿QQ号,但只有1G内存,如何实现去重?的更多相关文章
- STL容器及算法题:删除奇数的QQ号
最近思考到这样一个题目:在STL的set和vector容器里存储了1亿个QQ号,编写函数删除奇数QQ号. 1. STL容器简介 首先了解一下 set 和 vector 以及其他类似的 STL 容器: ...
- (转)最全正则表达式总结:验证QQ号、手机号、Email、中文、邮编、身份证、IP地址等
什么是 RegExp? RegExp 是正则表达式(Regular expression)的缩写,作用是对字符串执行模式匹配. 通常用于格式验证.正则替换.查找子串等 各种编程语言的正则表达式基本相同 ...
- 【面试被虐】如何只用2GB内存从20亿,40亿,80亿个整数中找到出现次数最多的数?
这几天小秋去面试了,不过最近小秋学习了不少和位算法相关文章,例如 [面试现场]如何判断一个数是否在40亿个整数中? [算法技巧]位运算装逼指南 对于算法题还是有点信心的,,,,于是,发现了如下对话. ...
- 用QQ号登陆Sharepoint,研究到最后关头卡住了。大家发力呀
此项目未完成,登陆不了SharePoint,大家研究吧,折腾吧..... 已经完成的部分有:已经可以获取到腾讯用户信息,如: Get Access Token===============access ...
- web安全:QQ号快速登录漏洞及被盗原理
为什么你什么都没干,但QQ空间中却发了很多小广告?也许你的QQ账号已经被盗.本文将讲解一个QQ的快速登录的漏洞. 我前阵子在论坛上看到一个QQ的快速登录的漏洞,觉得非常不错,所以把部分原文给转到园子来 ...
- [转帖]web安全:QQ号快速登录漏洞及被盗原理
web安全:QQ号快速登录漏洞及被盗原理 https://www.cnblogs.com/1996V/p/7481823.html 看了下 QQ的确监听 端口 大神牛B 自己这一块一直没深入学习过.. ...
- 如何从40亿整数中找到不存在的一个 webservice Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库 WPF实战案例-打印 RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
如何从40亿整数中找到不存在的一个 前言 给定一个最多包含40亿个随机排列的32位的顺序整数的顺序文件,找出一个不在文件中的32位整数.(在文件中至少确实一个这样的数-为什么?).在具有足够内存的情况 ...
- Problem H: 小姐姐的QQ号(DFS)
Contest - 河南省多校连萌(四) Problem H: 小姐姐的QQ号 Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 297 Solved: ...
- QQ号快速登录漏洞及被盗原理
web安全:QQ号快速登录漏洞及被盗原理 为什么你什么都没干,但QQ空间中却发了很多小广告?也许你的QQ账号已经被盗.本文将讲解一个QQ的快速登录的漏洞. 我前阵子在论坛上看到一个QQ的快速登录的 ...
- 队列解密QQ号
队列解密QQ号 本篇博客主要是<啊哈!算法>的读书笔记,这里做一下记录. 问题场景: 给定一串 QQ 号,631758924,从其中解密出真实的 QQ 号. 解密规则:首先将第一个数删除, ...
随机推荐
- Pandas 空值数据的索引 位置 行号
前言 先说一下什么是pandas, 这个东西其实就是一个处理表格数据的一个库.可以把它看做是一个没有图形化界面的Excel. Pandas中的空值是非常多的,这体现了数据搜集的一个不可避免的方面.由于 ...
- C#_自定义简单ORM
一,基本思路:利用C#的标签和反射功能实现自定义轻量级ORM 标签Attribute附着在实体属性或实体类名上,这样可以取到实体对应的表名,属性对应的表字段名,数据类型,是否主键,字段注释等基本信息 ...
- springboot 复杂邮件发送
application.yml配置 密码为邮箱开启smtp时邮箱服务商提供的密码
- Java面试题及答案整理汇总(2024最新版)
前言 辞退了老板,准备找下家,又要开始面试了,不得不准备准备八股文,还是很有必要针对性的刷一些题,很多朋友的实战能力很强,但是理论比较薄弱,要多准备准备理论知识,攻克面试官.这是我在全网寻找稍微比较完 ...
- MySQL,你只需看这一篇文章就够了!
MySQL--DAY02 distinct 去重 把查询结果去除重复记录[distinct] 注意:原表数据不会被修改,只是查询结果去重. 去重需要使用一个关键字:distinct mysql> ...
- 基于ctfshow的信息收集思路与CTF实战
本文靶场来源于CTFshow,并不完全按照靶机的顺序排列,而是以测试操作为导向,按博主个人理解排列. 1. 前端源码 在CTF中,先看源代码是个好习惯,出题者经常会在源代码中以注释的形式提供一些提示 ...
- javascript数组合并效率对比
1.数组元素量级大而合并次数少时,性能对比: concat() > push() > [-array1,-array2] 2.数组元素少但合并次数多时,性能对比: push() > ...
- python的egg的制作
egg包是目前最流行的python应用打包部署方式.如何制作和安装egg包?下面我就简单的分析了一下. 总是安装别人的egg包,是不是也想制作自己的egg包呢?好,接下来我们就自己制作一个简单的egg ...
- vue前端分页多条件搜索
vue前端分页多条件搜索 fliterData() { if (this.query_syscode || this.query_version || this.query_group || Stri ...
- Lombok 代码优化器
Lombok是一种Java实用工具,可用来帮助开发人员消除Java的冗长代码,尤其是对于简单的Java对象(POJO).它通过注释实现这一目的 使用安装Lombok pom文件导入lombok Mav ...