
本文针对使用apache poi逐行修改excel单元格时性能极低的问题,提供关键优化方案:避免在循环内重复打开/关闭文件流,并给出完整、安全、高效的代码实现。
在处理包含3万条记录的Excel文件(如formatted.xlsx)时,若采用每行更新后立即写入磁盘的方式,程序会因频繁的I/O操作而严重卡顿——原代码中FileOutputStream在循环内反复创建、workbook.write()反复调用、文件反复关闭,导致单行耗时约1秒,总耗时超1小时。根本原因并非POI本身慢,而是将磁盘写入操作置于高频循环中,违背了I/O优化的基本原则。
✅ 正确做法是:一次性加载工作簿 → 内存中批量修改 → 单次写入文件。以下是优化后的完整代码:
public static void main(String[] args) throws IOException {
String filePath = "formatted.xlsx";
// 1. 仅一次读取(注意:使用 try-with-resources 确保资源释放)
try (FileInputStream file = new FileInputStream(filePath);
XSSFWorkbook workbook = new XSSFWorkbook(file)) {
XSSFSheet sheet = workbook.getSheetAt(0);
int rowCount = sheet.getLastRowNum();
// 2. 遍历并修改单元格(纯内存操作,极快)
for (int i = 1; i <= rowCount; i++) {
XSSFRow row = sheet.getRow(i);
if (row == null) continue; // 跳过空行
Cell cell = row.getCell(3); // 第4列(索引3),即category列
if (cell != null && cell.getCellType() == CellType.STRING) {
String original = cell.getStringCellValue();
String updated = original.replace("#", "-");
cell.setCellValue(updated);
}
}
// 3. 仅一次写入磁盘(关键!移出循环)
try (FileOutputStream outFile = new FileOutputStream(filePath)) {
workbook.write(outFile);
}
System.out.println("✅ 更新完成:共处理 " + rowCount + " 行数据");
}
}? 关键优化点说明:
- ✅ I/O解耦:FileInputStream和FileOutputStream均只打开/关闭1次,彻底消除循环级磁盘开销;
- ✅ 资源安全:使用try-with-resources自动关闭流,避免资源泄漏;
- ✅ 健壮性增强:增加row == null和cell.getCellType() == CellType.STRING校验,防止NullPointerException或类型异常;
- ✅ 语义清晰:getLastRowNum()返回的是最大行索引(从0开始),因此遍历1到rowCount覆盖第2行至最后一行(首行为标题行);
⚠️ 额外建议(进阶提速):
立即学习“Java免费学习笔记(深入)”;
- 若仅需文本替换且无需保留公式/样式,可考虑切换至更轻量的库(如EasyExcel),其基于SAX解析,内存占用更低;
- 对于超大文件(>10万行),建议改用流式读写(SXSSFWorkbook)避免OOM;
- 避免在循环中调用System.out.println()——控制台输出本身也会显著拖慢速度,生产环境应移除或异步记录。
遵循以上优化,3万行Excel的处理时间通常可从1小时级降至10秒内,性能提升超300倍。核心原则始终不变:计算在内存,持久化在最后。











