场景题:假设有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 号. 解密规则:首先将第一个数删除, ...
随机推荐
- Cartographer学习——2D栅格地图构建
前言: 到目前为止,对于点云数据的预处理过程已经介绍完毕,如:点云数据多传感器时间同步.运动畸变校正.重力校正.体素滤波等.做完这一系列的预备工作之后,实际上呢,就可以进行点云的扫描匹配了. 在讲解扫 ...
- 快速激活JRebel的方法
当谈到 JRebel 的激活时,有几种方法可以让您在 IntelliJ IDEA 中使用它.以下是一些选项: 在线激活(推荐): 安装 JRebel 插件后,点击 Jrebel Activation ...
- 一些很好用的SVN功能
1.checkout 1.1 只checkout部分目录和文件 目的:有时候项目的文件很多,但是只会关心其中的某几个文件,就可以只checkout这几个文件,可以缩短checkout时间且减少其他文件 ...
- VMware安装教程---------------------以及Windows,Linux,Apple MAC OS系统安装
1.什么是VMware虚拟机 VMware虚拟机是一个虚拟机软件,它可以在一台机器上同时运行多个系统,这些系统包括Windows,Linux,Apple os等. 2.虚拟机有什么用 虚拟机的用处很多 ...
- Lua语法基础教程(上篇)
今天我们来学习Lua语法基础教程.由于篇幅过长,将分为上中下三篇进行讲解,本篇为上篇. 一.初识Lua Lua 是一种轻量小巧的脚本语言,它用标准C语言编写并以源代码形式开放.这意味着什么呢?这意味着 ...
- CodeForces - 1336A Linova and Kingdom
CodeForces - 1336A 就差一点点,很可惜,少发现个很显而易见的结论 就是一个点的价值,实际上就是(这个点的深度 - 之后的点的数目) 就是 \(depth_i - size_i\) 然 ...
- 【网关开发】Openresty使用cosocket API 发送http与tcp网络请求
背景 为网关提供健康检查功能时需要对节点发送http或者tcp探活请求.Openresty 提供cosocket来处理非阻塞IO. 实现 跟工程结合在一起,这里简单拼接数据结构 local funct ...
- JConsole 远程监控Tomcat服务
JConsole 远程监控Tomcat服务 1.概述 JConsole是一个基于JMX的GUI工具,用于连接正在运行的JVM,不过此JVM需要使用可管理的模式启动.如果要把一个应用以可管理的形式启动, ...
- Collections Framework中的算法(之三)--不可变装饰器相关
本篇主要讲述Collections类中的unmodifiable相关的方法!这些方法都有一个共同含义就是使用此方法创建的对象都是不可变的!典型的装饰器模式的应用!下面的几篇都是装饰器模式在Java C ...
- Collections Framework中的算法(之二)
从本篇开始我们讲述Collections中的一些算法的源代码!本篇主要讲述与排序相关的一些方法,如:排序.反序.反序比较器.乱序.最大值和最小值等. 一.头及一些与算法相关的属性 package ja ...