Java开发中处理Excel行的批量设值、批量取值、行复制、行清空等操作时,很多人会手写POI原生for循环,代码冗余、列索引易写错,还会频繁处理空指针问题。今天给大家介绍Hutool官方针对POI原生Row对象的轻量封装工具——RowUtil,一行代码搞定绝大多数单行操作。
开篇2个核心事实,从源头杜绝误导:
- 1. RowUtil是Hutool官方原生工具类,全限定名
cn.hutool.poi.excel.RowUtil,仅位于hutool-poi模块,需单独引入,绝非第三方非官方工具; - 2. 它的核心定位是简化POI原生Row对象的操作,覆盖单行批量读写、行复制/清空/删除、行数据转换等场景,不强制依赖Hutool ExcelWriter/ExcelReader,纯POI环境即可使用。
一、基础信息与依赖引入
1. 官方权威信息
| |
|---|
| Hutool 5.x/6.x 均位于 hutool-poi 模块 |
| |
| POI 4.0.0+(推荐使用POI 5.2.5稳定版) |
| https://doc.hutool.cn/module/poi/excel/RowUtil.html |
| https://github.com/dromara/hutool/blob/master/hutool-poi/src/main/java/cn/hutool/poi/excel/RowUtil.java |
2. 正确的依赖引入
Hutool-poi的POI依赖为optional可选依赖,必须显式引入POI相关依赖,否则会出现类缺失异常。以线上最稳定的Hutool 5.8.25 + POI 5.2.5为例,Maven依赖如下:
<!-- Hutool-poi 核心依赖(含RowUtil) -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-poi</artifactId>
<version>5.8.25</version>
</dependency>
<!-- POI 核心依赖(必须显式引入) -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.5</version>
</dependency>
二、核心功能与可复现示例
所有示例均基于Hutool 5.8.25 + POI 5.2.5验证,100%符合官方API签名,可直接复制运行;RowUtil已将POI操作的常见异常包装为运行时ExcelException,无需强制捕获受检异常。
前置说明:Row对象的获取
RowUtil仅操作POI原生org.apache.poi.ss.usermodel.Row对象,可通过3种合法方式获取,无强制依赖Hutool ExcelUtil:
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
public class RowInitTest {
public static void main(String[] args) {
// 方式1:纯POI原生创建
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("测试");
Row row = sheet.createRow(0); // 创建第1行(POI行/列索引均从0开始)
// 方式2:通过Hutool ExcelUtil获取
try (ExcelWriter writer = ExcelUtil.getWriter("test.xlsx")) {
Row row2 = writer.getSheet().createRow(0);
}
// 方式3:读取已有Excel获取行
try (Workbook readWorkbook = ExcelUtil.getReader("test.xlsx").getWorkbook()) {
Sheet readSheet = readWorkbook.getSheetAt(0);
Row existRow = readSheet.getRow(0); // 获取已存在的行
}
}
}
1. 核心功能1:单行批量设值(告别for循环)
这是RowUtil最常用的功能,支持按列索引批量设值、按表头名称批量设值,自动处理单元格类型转换、空值创建,列索引不会错位。
示例1:按列索引批量设值(最常用)
通过setRow(Row row, Collection<?> cellValues)实现,传入集合按索引顺序批量设值,集合的第N个元素,对应行的第N-1列(索引从0开始)。
import cn.hutool.poi.excel.RowUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import org.apache.poi.ss.usermodel.Row;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
public class RowSetByIndexTest {
public static void main(String[] args) {
try (ExcelWriter writer = ExcelUtil.getWriter("test_set_index.xlsx")) {
Row row = writer.getSheet().createRow(0);
// 按索引顺序批量设值,支持Java基本类型、包装类型、BigDecimal、LocalDate/LocalDateTime(POI 5.0+支持)
List<Object> cellValues = List.of(
"张三", 28, "研发部",
new BigDecimal("15000.50"), LocalDate.of(2020, 7, 1)
);
RowUtil.setRow(row, cellValues);
writer.flush();
}
}
}
补充规则:传入null值时,会创建空单元格,不会跳过列,避免出现列错位。
示例2:按表头名称批量设值
通过setRowByHeader(Row row, Map<String, ?> cellValues, Map<String, Integer> headerLocationMap)实现,无需关注列索引顺序,按表头名称自动匹配列。
核心前提:需提前获取表头名称→列索引的映射关系,纯POI即可生成,无需设置任何别名。
import cn.hutool.poi.excel.RowUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.Map;
public class RowSetByHeaderTest {
public static void main(String[] args) throws Exception {
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("员工表");
// 1. 先写入表头行(索引0),生成表头名称→列索引的映射
Row headerRow = sheet.createRow(0);
List<String> headers = List.of("姓名", "年龄", "部门", "薪资", "入职日期");
RowUtil.setRow(headerRow, headers);
// 2. 生成表头映射:key=表头名称,value=列索引
Map<String, Integer> headerLocationMap = new HashMap<>();
for (int i = 0; i < headers.size(); i++) {
headerLocationMap.put(headers.get(i), i);
}
// 3. 按表头名称批量设值,无需关注列顺序
Row dataRow = sheet.createRow(1);
Map<String, Object> data = Map.of(
"姓名", "李四",
"年龄", 32,
"部门", "产品部",
"薪资", new BigDecimal("18000.00"),
"入职日期", LocalDate.of(2019, 3, 15)
);
RowUtil.setRowByHeader(dataRow, data, headerLocationMap);
// 保存文件
try (FileOutputStream out = new FileOutputStream("test_set_header.xlsx")) {
workbook.write(out);
}
workbook.close();
}
}
2. 核心功能2:单行批量取值(告别getCell循环)
支持按索引全量/范围取值、按表头名称取值,自动处理空单元格、类型转换,无需手动处理空指针。
示例1:按索引批量取值
import cn.hutool.poi.excel.RowUtil;
import cn.hutool.poi.excel.ExcelUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import java.util.List;
public class RowGetByIndexTest {
public static void main(String[] args) {
try (Workbook workbook = ExcelUtil.getReader("test_set_index.xlsx").getWorkbook()) {
Sheet sheet = workbook.getSheetAt(0);
Row row = sheet.getRow(0);
// 1. 全量取值:获取行内所有单元格的值,返回List<Object>
List<Object> allValues = RowUtil.getRowValues(row);
System.out.println("全量值:" + allValues); // 输出:全量值:[张三, 28, 研发部, 15000.5, 2020-07-01]
// 2. 范围取值:获取索引0~3的单元格值(闭区间,包含首尾索引)
List<Object> rangeValues = RowUtil.getRowValues(row, 0, 3);
System.out.println("范围值:" + rangeValues); // 输出:范围值:[张三, 28, 研发部, 15000.5]
}
}
}
示例2:按表头名称批量取值
import cn.hutool.poi.excel.RowUtil;
import cn.hutool.poi.excel.ExcelUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class RowGetByHeaderTest {
public static void main(String[] args) {
try (Workbook workbook = ExcelUtil.getReader("test_set_header.xlsx").getWorkbook()) {
Sheet sheet = workbook.getSheetAt(0);
// 1. 读取表头行,生成表头名称→列索引的映射
Row headerRow = sheet.getRow(0);
List<Object> headers = RowUtil.getRowValues(headerRow);
Map<String, Integer> headerLocationMap = new HashMap<>();
for (int i = 0; i < headers.size(); i++) {
headerLocationMap.put(headers.get(i).toString(), i);
}
// 2. 按表头名称批量取值,返回Map<表头名称, 单元格值>
Row dataRow = sheet.getRow(1);
Map<String, Object> data = RowUtil.getRowValuesByHeader(dataRow, headerLocationMap);
System.out.println("按表头取值:" + data);
// 输出:按表头取值:{姓名=李四, 年龄=32, 部门=产品部, 薪资=18000.0, 入职日期=2019-03-15}
}
}
}
3. 核心功能3:行复制、行清空、行删除
示例1:行复制(同Sheet/跨Sheet,支持样式复制)
RowUtil提供了完整的行复制能力,必须显式指定是否复制样式,避免样式丢失。
import cn.hutool.poi.excel.RowUtil;
import cn.hutool.poi.excel.ExcelUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
public class RowCopyTest {
public static void main(String[] args) {
try (Workbook workbook = ExcelUtil.getReader("test_set_header.xlsx").getWorkbook()) {
Sheet sourceSheet = workbook.getSheetAt(0);
Row sourceRow = sourceSheet.getRow(0); // 源表头行(带样式)
// 1. 同Sheet复制:复制源行(索引0)到目标行(索引2),并复制样式
RowUtil.copyRow(sourceSheet, sourceRow, 2, true);
// 2. 跨Sheet复制:先创建目标Sheet,复制源行到目标Sheet的索引0位置,复制样式
Sheet targetSheet = workbook.createSheet("备份页");
RowUtil.copyRow(sourceSheet, targetSheet, 0, 0, true);
// 保存文件
ExcelUtil.writeWorkbook(workbook, "test_copy.xlsx");
}
}
}
补充说明:POI原生不支持跨Workbook复制行(样式、字体等资源属于当前Workbook),并非RowUtil不支持。
示例2:行清空、行删除
import cn.hutool.poi.excel.RowUtil;
import cn.hutool.poi.excel.ExcelUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
public class RowClearDeleteTest {
public static void main(String[] args) {
try (Workbook workbook = ExcelUtil.getReader("test_copy.xlsx").getWorkbook()) {
Sheet sheet = workbook.getSheetAt(0);
// 1. 行清空:清空行内所有单元格的内容和样式,保留行结构
Row row2 = sheet.getRow(2);
RowUtil.clearRow(row2);
// 2. 行删除:删除索引2的行,删除后下方行自动上移;若删除最后一行,仅清空内容
RowUtil.removeRow(sheet, 2);
// 保存文件
ExcelUtil.writeWorkbook(workbook, "test_clear_delete.xlsx");
}
}
}
三、避坑指南(必看,规避生产事故)
1. 索引规则避坑
POI原生的行、列索引均从0开始,RowUtil完全遵循该规则,不要混淆Excel界面的“第1行/第A列”(对应索引0)。
2. 按表头操作避坑
按表头名称设值/取值时,必须保证表头映射的名称与Excel表头完全一致(大小写、空格、特殊符号均严格匹配),否则会出现列找不到的异常。
3. 行复制避坑
- • 同Sheet复制时,若目标行已存在数据,会直接覆盖;
- • 只有显式传入
copyStyle=true时,才会复制单元格样式,默认不复制样式; - • 合并单元格无法通过copyRow直接复制,需额外处理合并区域。
4. 性能避坑
- • RowUtil是单行操作工具,10万行以上的大批量数据处理,优先使用EasyExcel流式读写,或POI原生SXSSFWorkbook,避免内存溢出;
- • 批量处理多行时,提前生成表头映射,不要在循环内重复创建映射对象,避免性能损耗。
5. 类型兼容避坑
- • POI 4.x及以下版本不支持直接写入
LocalDate/LocalDateTime,需转为java.util.Date; - • 数值类型写入时,会自动适配单元格类型,避免出现文本格式的数字。
四、总结
Hutool RowUtil的核心价值,是把POI原生繁琐的单行操作(批量设值、批量取值、行复制、行清空等)封装为一行代码,告别重复的for循环,大幅降低列索引写错、空指针等问题的概率。
它的定位是POI Row对象的辅助工具,不强制依赖Hutool的Excel读写能力,纯POI环境即可使用,完美适配已有POI项目的轻量化改造。如果你在项目中需要频繁处理Excel行操作,不想手写冗余的POI原生代码,RowUtil是兼顾便捷性与稳定性的最优选择。
参考来源(全部为官方权威文档):
- 2. Apache POI 5.2.5 官方API文档