Java读取超大型CSV文件,按访问方式统计子用户数量
大CSV文件子用户访问方式统计实现方案
核心思路
因为文件超2GB、2000万+记录,绝对不能一次性把整个文件加载到内存,必须用流式逐行解析,配合轻量的统计容器(子用户仅200+,内存占用可忽略)。
具体实现步骤
1. 选择轻量CSV解析库
推荐用Apache Commons CSV,它支持流式读取,避免内存溢出。Maven依赖如下:
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-csv</artifactId> <version>1.10.0</version> </dependency>
2. 流式读取+统计代码
假设CSV列格式为子用户ID,访问方式,代码逻辑如下(可根据实际列名调整):
import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; import java.io.FileReader; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class CsvUserStats { public static void main(String[] args) { // 初始化统计Map,已知子用户200+,设初始容量300避免扩容开销 Map<String, Integer> userCountMap = new HashMap<>(300); String csvFilePath = "你的目标文件路径.csv"; try (FileReader reader = new FileReader(csvFilePath); // 配置CSV格式,指定表头列名,自动跳过第一行表头 CSVParser parser = CSVFormat.DEFAULT .withHeader("子用户ID", "访问方式") .withFirstRecordAsHeader() .parse(reader)) { // 逐行解析处理 for (CSVRecord record : parser) { String userId = record.get("子用户ID"); // 按子用户统计总访问数(若需按访问方式细分,可将键设为userId+"_"+访问方式) userCountMap.put(userId, userCountMap.getOrDefault(userId, 0) + 1); } // 输出统计结果 for (Map.Entry<String, Integer> entry : userCountMap.entrySet()) { System.out.println(entry.getKey() + "计数" + entry.getValue()); } } catch (IOException e) { e.printStackTrace(); } } }
3. 关键优化点
- 流式处理:用
try-with-resources自动管理流资源,逐行读取解析,内存仅占用当前行数据+统计Map(几KB级别) - 编码适配:若CSV为GBK等非UTF-8编码,替换
FileReader为new InputStreamReader(new FileInputStream(csvFilePath), "GBK") - 无效行过滤:循环内可增加判断,跳过子用户ID为空的无效行
- 线程安全(可选):若用并行流处理,将
HashMap替换为ConcurrentHashMap保证线程安全
4. 原生Java备用方案(无第三方库)
若不想引入依赖,可手动逐行读取,但需注意CSV字段含逗号的场景(比如引号包裹的字段)会出错,仅适用于简单格式:
import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class RawCsvStats { public static void main(String[] args) { Map<String, Integer> userCountMap = new HashMap<>(300); String csvFilePath = "你的目标文件路径.csv"; try (BufferedReader br = new BufferedReader(new FileReader(csvFilePath))) { // 跳过表头 br.readLine(); String line; while ((line = br.readLine()) != null) { String[] parts = line.split(","); if (parts.length >= 2) { String userId = parts[0].trim(); userCountMap.put(userId, userCountMap.getOrDefault(userId, 0) + 1); } } // 输出结果 userCountMap.forEach((k, v) -> System.out.println(k + "计数" + v)); } catch (IOException e) { e.printStackTrace(); } } }
注意事项
- 避免用Excel类库(如POI)处理大CSV,这类库内存开销极大
- 无需额外调大JVM内存,默认堆内存即可支撑
- 串行版本处理2000万条记录仅需数分钟,新手优先保证串行版本跑通再考虑并行优化
内容的提问来源于stack exchange,提问作者Runtime21
相关产品推荐
相关产品推荐

