You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apache POI删除指定行并追加行异常问题咨询

嘿,我仔细梳理了你的测试结果和需求,咱们一步步拆解这些问题:

一、为什么HSSF执行appendRow后不更新lastRowNum?

这是HSSF(对应旧版.xls格式)和XSSF(新版.xlsx)的底层机制差异导致的:

  • XSSF的getLastRowNum()会实时跟踪逻辑上的最后一行索引,创建新行后会自动更新数值。
  • 而HSSF的getLastRowNum()返回的是最后一个存在的物理行的索引,它不会自动清理行删除后留下的“索引空洞”。比如你删除行后,原来的最后一行索引对应的行可能只是被清空但仍存在于工作表的行集合中,所以getLastRowNum()还是返回原来的数值;即使你创建了新行,HSSF也不会立即更新这个值,除非你显式触发行集合的排序或重新读取文件。

二、如何用shiftRows2 + POI 3.16实现需求?

根据你的测试,3.16版本的XSSF用shiftRows2 + appendRow是正常的,但要适配HSSF并确保逻辑严谨,正确的操作流程应该是这样的(核心是先保存目标行内容,移位后再把内容放到最后,最后追加新数据):

完整实现步骤

假设你的需求是:把指定行rowNum从原位置移除(后面的行上移补位),将该行内容移到当前最后一行,然后在最后一行后追加新数据。

1. 基础准备与边界判断

int rowNum = // 你要移动的目标行索引
Sheet sheet = workbook.getSheetAt(0);
int originalLastRow = sheet.getLastRowNum();

// 如果目标行已经是最后一行,直接追加数据即可,不用移位
if (rowNum == originalLastRow) {
    appendRow(sheet.getLastRowNum() + 1, objectArray);
    return;
}

2. 保存目标行内容并执行移位

这里的shiftRows2作用是把rowNum+1到originalLastRow的行向上移1行,填补原rowNum的位置,同时空出原来的最后一行位置:

// 先保存要移动的行的完整内容
Row targetRow = sheet.getRow(rowNum);
// 执行移位:后面的行上移,空出最后一行
sheet.shiftRows(rowNum + 1, originalLastRow, -1, true, false);
// 把目标行内容复制到空出来的最后一行位置
Row newLastRow = sheet.getRow(originalLastRow);
if (newLastRow == null) {
    newLastRow = sheet.createRow(originalLastRow);
}
// 调用自定义的行复制方法(见下方)
copyRow(targetRow, newLastRow, workbook);

3. 追加新数据

此时工作表的最后一行是originalLastRow,直接在它后面创建新行即可:

appendRow(sheet.getLastRowNum() + 1, objectArray);

4. 针对HSSF的额外处理

如果是HSSF格式,执行完上述步骤后getLastRowNum()可能还是旧值,需要手动触发行集合排序来更新索引:

// 追加新行后,强制HSSF更新行索引
if (sheet instanceof HSSFSheet) {
    ((HSSFSheet) sheet).getRowRecords().sort(Comparator.comparingInt(RowRecord::getRowNumber));
}

必备的行复制工具方法

POI没有内置的完整行复制方法,需要自己实现一个,确保单元格的内容、样式、格式都被正确复制:

private void copyRow(Row sourceRow, Row targetRow, Workbook workbook) {
    // 复制行高
    targetRow.setHeight(sourceRow.getHeight());
    // 遍历所有单元格
    for (int cellIdx = sourceRow.getFirstCellNum(); cellIdx < sourceRow.getLastCellNum(); cellIdx++) {
        Cell sourceCell = sourceRow.getCell(cellIdx);
        Cell targetCell = targetRow.createCell(cellIdx);
        if (sourceCell == null) {
            continue;
        }
        // 复制单元格值(适配各种数据类型)
        switch (sourceCell.getCellType()) {
            case STRING:
                targetCell.setCellValue(sourceCell.getStringCellValue());
                break;
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(sourceCell)) {
                    targetCell.setCellValue(sourceCell.getDateCellValue());
                } else {
                    targetCell.setCellValue(sourceCell.getNumericCellValue());
                }
                break;
            case BOOLEAN:
                targetCell.setCellValue(sourceCell.getBooleanCellValue());
                break;
            case FORMULA:
                targetCell.setCellFormula(sourceCell.getCellFormula());
                break;
            case BLANK:
                targetCell.setBlank();
                break;
            default:
                targetCell.setCellValue(sourceCell.getStringCellValue());
        }
        // 复制单元格样式
        targetCell.setCellStyle(sourceCell.getCellStyle());
    }
}

这样处理后,无论是XSSF还是HSSF,在POI 3.16版本下都能稳定实现你的需求:将指定行移至最后,然后追加新数据。

内容的提问来源于stack exchange,提问作者xmcx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 22:37:40