超30万行CSV文件匹配输出异常问题技术求助
大CSV文件匹配后输出异常的问题分析与解决
问题场景
读取两个CSV文件存入ArrayList后进行内容匹配,小文件(行数≤20)处理正常,但当其中一个CSV超过30万行时,匹配结果输出异常:1.5万行的РФМ.csv内容可完整显示,30万+行的БД.csv仅输出299000行后的内容,但单独输出listBD索引0-299000的元素均正常。两个文件包含姓名与出生日期数据,未排序。
原代码如下:
public class Podft { public static void main(String[] args) throws IOException{ ArrayList<String> listBD = new ArrayList<>(); ArrayList<String> listPFM = new ArrayList<>(); ArrayList<String> result = new ArrayList<>(); BufferedReader readerBD = new BufferedReader(new FileReader("C:\Users\d\Doc\БД.csv")); BufferedReader readerPFM = new BufferedReader(new FileReader("C:\Users\d\Doc\РФМ.csv")); String lineBD = readerBD.readLine(); String linePFM = readerPFM.readLine(); while (lineBD != null) { listBD.add(lineBD); lineBD = readerBD.readLine(); } while (linePFM != null) { listPFM.add(linePFM); linePFM = readerPFM.readLine(); } for(int i = 0; i < listBD.size(); i++){ for(int j = 0; j < listPFM.size(); j++){ if(listBD.get(i).equals(listPFM.get(j))){ result.add(listPFM.get(j)); } } } for (String q : result){ System.out.println(q); } } }
问题原因
- 控制台输出缓冲区限制:绝大多数终端(Windows命令提示符、IDEA默认控制台等)都有输出行数/缓冲区大小限制,当输出内容过多时,早期的输出会被覆盖或丢弃,只能看到最后一部分内容。这是最核心的原因——并非程序未生成前面的结果,而是控制台无法显示全部内容。
- 双重循环的性能灾难:30万行×1.5万行=4.5亿次循环,会导致程序运行极慢,甚至因内存波动间接影响输出稳定性。
- 资源未正确回收:代码未手动关闭
BufferedReader,大文件场景下可能引发资源泄漏,导致未知异常。
解决方案
1. 替换控制台输出:将结果写入文件
直接输出到控制台受限于缓冲区,改为写入文件即可查看完整结果:
// 替换原输出循环 try (BufferedWriter writer = new BufferedWriter(new FileWriter("C:\\Users\\d\\Doc\\匹配结果.csv"))) { for (String q : result) { writer.write(q); writer.newLine(); } } catch (IOException e) { e.printStackTrace(); }
2. 优化匹配性能:用HashSet替代ArrayList查找
将较小的集合(РФМ.csv)存入HashSet,把匹配时间复杂度从O(m*n)降至O(m),性能提升几个数量级:
// 将listPFM转为HashSet HashSet<String> pfmSet = new HashSet<>(listPFM); // 匹配逻辑简化为单循环 for (String bdLine : listBD) { if (pfmSet.contains(bdLine)) { result.add(bdLine); } }
3. 改进资源管理:使用try-with-resources自动关闭流
避免手动管理流的关闭,防止资源泄漏:
// 读取БД.csv try (BufferedReader readerBD = new BufferedReader(new FileReader("C:\\Users\\d\\Doc\\БД.csv"))) { String lineBD; while ((lineBD = readerBD.readLine()) != null) { listBD.add(lineBD); } } // 读取РФМ.csv try (BufferedReader readerPFM = new BufferedReader(new FileReader("C:\\Users\\d\\Doc\\РФМ.csv"))) { String linePFM; while ((linePFM = readerPFM.readLine()) != null) { listPFM.add(linePFM); } }
4. 可选:用专业CSV库处理复杂场景
如果CSV存在多行字段、分隔符转义等情况,手动读取行可能出错,推荐使用OpenCSV等库按字段处理:
// 示例:用OpenCSV读取并拼接匹配键 try (CSVReader readerBD = new CSVReader(new FileReader("C:\\Users\\d\\Doc\\БД.csv"))) { String[] nextLine; while ((nextLine = readerBD.readNext()) != null) { // 按姓名+出生日期拼接匹配键 String key = nextLine[0] + "," + nextLine[1]; listBD.add(key); } }
整合后的完整代码
import java.io.*; import java.util.ArrayList; import java.util.HashSet; public class Podft { public static void main(String[] args) throws IOException { ArrayList<String> listBD = new ArrayList<>(); ArrayList<String> listPFM = new ArrayList<>(); ArrayList<String> result = new ArrayList<>(); // 读取БД.csv,自动关闭流 try (BufferedReader readerBD = new BufferedReader(new FileReader("C:\\Users\\d\\Doc\\БД.csv"))) { String lineBD; while ((lineBD = readerBD.readLine()) != null) { listBD.add(lineBD); } } // 读取РФМ.csv,自动关闭流 try (BufferedReader readerPFM = new BufferedReader(new FileReader("C:\\Users\\d\\Doc\\РФМ.csv"))) { String linePFM; while ((linePFM = readerPFM.readLine()) != null) { listPFM.add(linePFM); } } // 用HashSet优化匹配性能 HashSet<String> pfmSet = new HashSet<>(listPFM); for (String bdLine : listBD) { if (pfmSet.contains(bdLine)) { result.add(bdLine); } } // 将结果写入文件,避免控制台输出限制 try (BufferedWriter writer = new BufferedWriter(new FileWriter("C:\\Users\\d\\Doc\\匹配结果.csv"))) { for (String q : result) { writer.write(q); writer.newLine(); } } // 输出匹配结果数量到控制台 System.out.println("匹配完成,共找到 " + result.size() + " 条匹配记录"); } }
内容的提问来源于stack exchange,提问作者Дмитрий Сергеевич
相关产品推荐
相关产品推荐

