场景题 如何Java用内存200M的情况下读取1G文件,并统计重复内容?
目录
一、流式读取与内存控制
二、高效去重数据结构
三、内存优化补充策略
四、性能与监控
五、完整示例代码
引用与扩展
最近在面试的时候碰到的一个场景题
在Java中处理1GB大文件并统计重复内容,需结合流式读取和内存优化技术。根据内存200MB限制,以下是分步骤解决方案及关键技术点:
一、流式读取与内存控制
逐行读取技术
使用
BufferedReader
或Scanner
实现流式处理,避免一次性加载文件。示例代码:
try (BufferedReader br = new BufferedReader(new FileReader("large.txt"), 8192)) {String line;while ((line = br.readLine()) != null) {// 处理每行数据 }
}
-
- 内存消耗:仅需约20MB缓冲(取决于缓冲区大小)。
- 字符编码处理
明确指定编码(如UTF-8)避免解析错误:
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)
);
二、高效去重数据结构
- BitSet去重法
-
- 原理:每个数字对应一个二进制位,若位值为1则表示已存在,否则标记为1。
- 内存优势:9亿数字仅需约112MB内存(9e8 bits ≈ 107MB)。
- 示例代码:
BitSet bitSet = new BitSet();
int num = Integer.parseInt(line);
if (bitSet.get(num)) {// 统计重复 duplicateCount++;
} else {bitSet.set(num);
}
- 适用条件
-
- 数字范围在整数范围内(0~2^31-1)。
- 若数字范围过大,需分片处理(如哈希分片到多个BitSet)。
三、内存优化补充策略
- 分治处理(可选)
若数字范围超限,采用哈希分片写入临时文件,再逐个处理小文件:
int hash = Math.abs(line.hashCode()) % 1000;
writeToTempFile(line, "shard_" + hash + ".tmp");
- Bloom Filter(允许误判时)
若允许一定误判率,使用布隆过滤器进一步压缩内存:
BloomFilter<Integer> filter = BloomFilter.create(Funnels.integerFunnel(), 1e9, 0.01);
if (filter.mightContain(num)) {// 可能重复
}
四、性能与监控
- 垃圾回收调优
-
- 年轻代(Young Generation)设置为较小空间(如50MB),减少Full GC频率。
- JVM参数示例:
-Xmx200m -XX:NewSize=50m
。
- 监控工具
使用VisualVM或JConsole观察内存占用,确保老年代(Old Generation)稳定在130MB以内。
五、完整示例代码
public class Deduplicator {public static void main(String[] args) throws IOException {BitSet bitSet = new BitSet();int duplicates = 0;try (BufferedReader br = new BufferedReader(new FileReader("large.txt"), 8192)) {String line;while ((line = br.readLine()) != null) {int num = Integer.parseInt(line.trim()); if (bitSet.get(num)) {duplicates++;} else {bitSet.set(num); }}}System.out.println(" 重复次数: " + duplicates);}
}
引用与扩展
- 关键参考:验证了BitSet方案的实际内存占用,提供了流式读取的优化方法,总结了避免内存溢出的通用原则。
- 扩展场景:若需排序,可结合外排序(External Sort)和归并策略。