1. 概述
Apache POI 是一个开源库,允许我们以编程方式操作 Microsoft Office 文档,包括 Excel 文件。Apache POI 提供了三个不同的类来创建工作簿:HSSFWorkbook、XSSFWorkbook 和 SXSSFWorkbook。
本文将深入对比这三个类的功能特性,并通过实际测试数据帮助您根据具体场景选择最佳方案。
2. 创建 Excel 文件
在对比之前,先快速回顾如何使用 Apache POI 生成 Excel 文件。首先在 pom.xml 中添加 Apache POI 和 POI OOXML schema 依赖:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.3.0</version>
</dependency>
创建 Workbook 实例只需调用目标类的构造函数,然后向工作簿写入内容,最后输出到目标流。以下示例使用 XSSFWorkbook 创建包含测试数据的 test.xlsx 文件:
try (Workbook workbook = new XSSFWorkbook();
OutputStream outputStream = new BufferedOutputStream(new FileOutputStream("test.xlsx"))) {
Sheet sheet = workbook.createSheet("test");
sheet.createRow(0).createCell(0).setCellValue("test content");
workbook.write(outputStream);
}
✅ 切换工作簿类非常简单:只需替换 XSSFWorkbook 为其他实现类,后续操作(创建工作表、行、单元格)完全一致。
⚠️ 注意文件扩展名:
- HSSFWorkbook 生成旧版 .xls 格式(Excel 97-2003)
- XSSFWorkbook 和 SXSSFWorkbook 生成新版 .xlsx 格式(Excel 2007+)
3. 核心特性对比
Apache POI 的三个工作簿类既有共性又有显著差异,下表总结了关键特性:
特性 | HSSFWorkbook | XSSFWorkbook | SXSSFWorkbook |
---|---|---|---|
文件格式 | .xls (BIFF8 二进制) | .xlsx (OpenXML) | .xlsx (OpenXML) |
单表最大行数 | 65,536 | 1,048,576 | 1,048,576 |
单表最大列数 | 256 | 16,384 | 16,384 |
流式处理 | ❌ 否 | ❌ 否 | ✅ 是 |
内存占用 | 高 | 高 | 低 |
核心差异:
- HSSFWorkbook 生成旧版二进制格式,适用于兼容性场景
- XSSFWorkbook 和 SXSSFWorkbook 生成新版 XML 格式
- HSSFWorkbook 和 XSSFWorkbook 是非流式工作簿,所有数据驻留内存
- SXSSFWorkbook 是流式工作簿,仅保留部分行在内存中
4. SXSSFWorkbook 的功能限制
SXSSFWorkbook 采用流式处理:当内存中的行数达到窗口阈值(默认 100)时,会自动将数据刷新到磁盘。这种机制导致某些功能无法正常使用——踩坑预警!
4.1 测试环境搭建
创建测试用例验证受限功能,初始化包含两行数据的 SXSSFWorkbook,显式设置窗口大小为 1(实际场景建议更大):
@BeforeEach
void setup() {
workbook = new SXSSFWorkbook(1);
sheet = workbook.createSheet("Test Sheet");
sheet.createRow(0).createCell(0).setCellValue(5);
sheet.createRow(1).createCell(0).setCellValue(15);
}
4.2 自动调整列宽
自动调整列宽功能会根据单元格内容长度设置列宽,但在流式模式下会失败——因为系统无法获取已刷新行的宽度信息:
@Test
void whenAutoSizeColumnOnSXSSFWorkbook_thenThrowsIllegalStateException() {
assertThrows(IllegalStateException.class, () -> sheet.autoSizeColumn(0));
}
4.3 克隆工作表
克隆工作表功能需要完整内存数据支持,流式模式下无法实现:
@Test
void whenCloneSheetOnSXSSFWorkbook_thenThrowsIllegalStateException() {
assertThrows(IllegalStateException.class, () -> workbook.cloneSheet(0));
}
4.4 获取行数据
尝试获取已刷新的行会返回 null:
@Test
void whenGetRowOnSXSSFWorkbook_thenReturnNull() {
Row row = sheet.getRow(0);
assertThat(row).isNull();
}
⚠️ 如果窗口大小设为 2(未刷新),此测试会返回行对象。
4.5 公式计算
公式计算功能要求相关行在内存中,否则会抛出异常:
@Test
void whenEvaluateFormulaCellOnSXSSFWorkbook_thenThrowsIllegalStateException() {
Cell formulaCell = sheet.createRow(sheet.getLastRowNum()).createCell(0);
formulaCell.setCellFormula("SUM(A1:B1)");
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
assertThrows(SXSSFFormulaEvaluator.RowFlushedException.class,
() -> evaluator.evaluateFormulaCell(formulaCell));
}
4.6 列位移操作
列位移需要完整工作表数据支持,流式模式下直接抛出不支持异常:
@Test
void whenShiftColumnsOnSXSSFWorkbook_thenThrowsUnsupportedOperationException() {
assertThrows(UnsupportedOperationException.class, () -> sheet.shiftColumns(0, 2, 1));
}
5. 性能评估
通过 JMH (Java Microbenchmark Harness) 对执行时间和内存消耗进行量化测试。
5.1 测试方案
创建包含指定行数的工作表,每行 256 列,填充相同文本内容:
Sheet sheet = workbook.createSheet();
for (int n=0;n<iterations;n++) {
Row row = sheet.createRow(sheet.getLastRowNum()+1);
for (int c=0;c<256;c++) {
Cell cell = row.createCell(c);
cell.setCellValue("abcdefghijklmnopqrstuvwxyz");
}
}
测试数据量:2,500 / 5,000 / 10,000 / 20,000 / 40,000 行。每组测试执行三次取平均值。
5.2 执行时间对比(毫秒)
行数 | HSSFWorkbook | XSSFWorkbook | SXSSFWorkbook |
---|---|---|---|
2,500 | 73 | 2,658 | 296 |
5,000 | 174 | 4,522 | 612 |
10,000 | 347 | 10,994 | 1,808 |
20,000 | 754 | 21,733 | 3,751 |
40,000 | 1,455 | 42,331 | 7,342 |
关键发现:
- HSSFWorkbook 速度最快,比 XSSFWorkbook 快约 30 倍
- XSSFWorkbook 性能最差,数据量越大劣势越明显
- SXSSFWorkbook 性能居中,但显著优于 XSSFWorkbook
原因分析:二进制 .xls 格式处理复杂度远低于 XML 格式,数据量越大性能差距越悬殊。
5.3 内存消耗对比(MB)
行数 | HSSFWorkbook | XSSFWorkbook | SXSSFWorkbook |
---|---|---|---|
2,500 | 828 | 1,871 | 258 |
5,000 | 1,070 | 2,926 | 212 |
10,000 | 1,268 | 4,136 | 209 |
20,000 | 1,766 | 7,443 | 209 |
40,000 | 1,475 | 10,119 | 210 |
关键发现:
- HSSFWorkbook 和 XSSFWorkbook 内存消耗随数据量线性增长
- XSSFWorkbook 内存占用是 HSSFWorkbook 的 2-7 倍
- SXSSFWorkbook 内存占用稳定在 ~210MB,几乎不受数据量影响
根本原因:流式处理机制使 SXSSFWorkbook 仅保留少量行在内存中,是处理海量数据的理想选择。
6. 总结建议
根据场景选择合适的工作簿实现:
✅ HSSFWorkbook 适用场景:
- 需要兼容旧版 Excel(97-2003)
- 数据量小(<65k 行)
- 对性能要求极高
✅ XSSFWorkbook 适用场景:
- 需要完整 Excel 功能支持
- 数据量中等(内存充足)
- 不需要流式处理
✅ SXSSFWorkbook 适用场景:
- 处理超大数据集(>100k 行)
- 内存资源受限
- 可接受部分功能限制
决策树:
- 需要兼容旧 Excel? → HSSFWorkbook
- 需要完整功能且内存足够? → XSSFWorkbook
- 处理大数据且内存有限? → SXSSFWorkbook