问题背景
假设我们需要对40亿个QQ号进行去重处理,但可用内存限制为1GB。常规方法(如HashSet或全量排序)在内存占用上存在明显瓶颈,因此需要设计一个兼顾内存效率和执行效率的解决方案。
核心挑战
-
内存限制:40亿个QQ号存储为
long
类型需约32GB(40亿×8字节),远超1GB内存。 -
去重准确性:必须保证100%准确,不能容忍误判。
-
执行效率:需尽量减少磁盘I/O和计算时间。
方案选型分析
方法 | 优点 | 缺点 |
---|---|---|
全量排序 | 结果精确 | 内存不足,磁盘I/O高 |
布隆过滤器 | 内存效率高 | 存在误判,不适用 |
位图法 | 查询效率O(1) | 依赖号码分布范围 |
分治法 | 内存可控 | 需多次磁盘I/O |
结论:分治法(Hash分片+内存去重)是唯一可行方案。
思考:如果QQ号范围已知,如何进一步优化内存?欢迎在评论区分享你的想法!
分治法实现步骤
1. 计算分片数量
• 内存估算:假设每个QQ号(long
类型)在HashSet
中占用20字节(包含对象开销),1GB内存可存储约5千万个QQ号。
• 分片数:40亿 / 5千万 = 80
,建议分片数为100以留出冗余。
2. 哈希分片写入文件
// 使用取模哈希分片到100个文件
public void shardQQNumbers(String inputFile, int shardCount) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(inputFile));
BufferedWriter[] writers = new BufferedWriter[shardCount];
for (int i = 0; i < shardCount; i++) {
writers[i] = new BufferedWriter(new FileWriter("shard_" + i + ".txt"));
}
String line;
while ((line = reader.readLine()) != null) {
long qq = Long.parseLong(line);
int shardId = (int) (qq % shardCount); // 简单哈希函数
writers[shardId].write(line + "\n");
}
// 关闭所有writer和reader
}
3. 逐个分片去重
public void deduplicateShard(int shardId) throws IOException {
Set<Long> uniqueQQs = new HashSet<>();
BufferedReader reader = new BufferedReader(new FileReader("shard_" + shardId + ".txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("dedup_" + shardId + ".txt"));
String line;
while ((line = reader.readLine()) != null) {
long qq = Long.parseLong(line);
if (uniqueQQs.add(qq)) { // 自动去重
writer.write(line + "\n");
}
}
// 关闭reader和writer
}
4. 合并结果
将所有dedup_*.txt
合并为最终文件,因相同QQ号必在同一分片,无需二次去重。
优化策略
-
并行处理分片:利用多线程同时处理多个分片文件。
-
压缩中间文件:使用GZIP压缩分片文件,减少磁盘占用。
-
内存优化:改用
long
数组+位操作替代HashSet
(需号码范围已知)。