本文介绍一种基于 java 的可靠方案:将 excel 数据映射为 pojo 对象、按业务主键排序后逐行比对,从而精准识别新增、删除与字段变更,彻底规避 apache poi 行号硬绑定导致的误判问题。
本文介绍一种基于 java 的可靠方案:将 excel 数据映射为 pojo 对象、按业务主键排序后逐行比对,从而精准识别新增、删除与字段变更,彻底规避 apache poi 行号硬绑定导致的误判问题。
在实际业务中(如财务对账、版本审计或数据迁移),常需比对两个结构相同但行顺序不同的 Excel 文件,并精确提取字段级变化(例如某单元格由“Pending”变为“Approved”)。若直接使用 Apache POIO 逐行比对(Sheet.getRow(i)),会因行序错位导致大量“假差异”,完全失效。
✅ 正确思路是:脱离物理行号,转向语义化比对。核心步骤如下:
解析 Excel → 构建 POJO 列表
使用 Apache POI 读取每个文件,将每行数据(如 ID、Name、Status)封装为统一 POJO(例如 Record 类),并确保关键字段(如 id)作为业务主键:
public class Record { private String id; private String name; private String status; // constructor, getters, setters, equals/hashCode based on 'id'}
按主键排序 → 对齐逻辑顺序
对两个 List<Record> 分别调用 Collections.sort() 或 list.sort(Comparator.comparing(Record::getId)),使相同 ID 的记录在两个列表中处于相同索引位置。
双指针/Map 方式比对 → 提取真实变更
推荐使用 Map<String, Record> 提升效率(O(n)):
Map<String, Record> oldMap = oldList.stream().collect(Collectors.toMap(Record::getId, r -> r));Map<String, Record> newMap = newList.stream().collect(Collectors.toMap(Record::getId, r -> r));// 新增、删除、修改分别处理newMap.forEach((id, newRec) -> { Record oldRec = oldMap.get(id); if (oldRec == null) { System.out.println("新增: " + newRec); } else if (!oldRec.equalsContent(newRec)) { // 自定义字段级比较 System.out.println("变更: " + id + " → Status从'" + oldRec.getStatus() + "'→'" + newRec.getStatus() + "'"); }});oldMap.keySet().stream().filter(id -> !newMap.containsKey(id)) .forEach(id -> System.out.println("删除: " + oldMap.get(id)));
⚠️ 关键注意事项:
最终,将差异结果(List<DiffResult>)导出为新 Excel,即可生成清晰的变更报告。该方法不依赖行序,稳定、可扩展,且天然支持后续加入字段映射、忽略列、阈值过滤等增强功能。