
当使用 apache poi 等库读取 excel 文件,并将每一行数据转换为一个 map<string, string> 对象时,通常会遇到一个问题:java 的 hashmap 默认不保证元素的插入顺序。这意味着,即使 excel 表格中的列是按特定顺序(例如,“列1”、“列2”)排列的,当这些列名及其对应的值被放入 hashmap 后,从 hashmap 中取出时的顺序可能与原始顺序不符。
例如,原始 Excel 数据如下:
column1 column2 value1 value2 value3 value4
如果使用 HashMap 存储,结果可能变为:
0 = "column2" -> value2
"column1" -> value1
1 = "column2" -> value4
"column1" -> value3这种无序性在需要严格保持列顺序的场景(如重新写入 Excel、数据比对或特定业务逻辑处理)下,会带来极大的不便甚至错误。
为了解决 HashMap 的无序问题,Java 提供了 LinkedHashMap。LinkedHashMap 是 HashMap 的子类,它通过维护一个双向链表来记录元素的插入顺序。这意味着,当您向 LinkedHashMap 中添加键值对时,它们会按照添加的顺序被保留。当您遍历 LinkedHashMap 时,元素将按照它们被插入的顺序返回。
立即学习“Java免费学习笔记(深入)”;
对于需要对键进行自然排序或自定义排序的场景,TreeMap 也是一个选择。TreeMap 会根据键的自然顺序或提供的 Comparator 进行排序。然而,对于保持原始的、非字母顺序的列顺序,LinkedHashMap 是更直接和高效的选择。
以下是如何修改现有的 Excel 读取方法,以使用 LinkedHashMap 来保持列的原始顺序:
import org.apache.poi.ss.usermodel.*;
import java.util.*;
import java.util.stream.Collectors;
public class ExcelReader {
/**
* 从给定的 Excel Sheet 中读取数据,并将其存储为 List<Map<String, String>>。
* 每个 Map 保持原始列的插入顺序。
*
* @param sheet 要读取的 Excel Sheet 对象。
* @return 包含所有行数据的 List,每行数据是一个保持列顺序的 Map。
* 如果 Sheet 为空,则返回一个空列表。
*/
public static List<Map<String, String>> readExcelSheetOrdered(Sheet sheet) {
Iterator<Row> rows = sheet.iterator();
// 检查 Sheet 是否为空
if (!rows.hasNext()) {
return Collections.emptyList();
}
// 读取标题行
Row header = rows.next();
List<String> keys = new ArrayList<>();
// 遍历标题单元格,获取列名。
// 注意:这里假设列名是连续的,遇到空列名则停止。
for (Cell cell : header) {
String value = cell.getStringCellValue().trim(); // 获取并修剪列名
if (!value.isEmpty()) {
keys.add(value);
} else {
// 如果遇到空列名,则认为后续没有有效列,停止读取标题
break;
}
}
// 存储所有行数据的列表
List<Map<String, String>> result = new ArrayList<>();
// 遍历数据行
while (rows.hasNext()) {
Row row = rows.next();
// 使用 LinkedHashMap 替代 HashMap,以保持列的插入顺序
Map<String, String> rowMap = new LinkedHashMap<>();
// 遍历每一列,根据标题行的顺序填充数据
for (int i = 0; i < keys.size(); ++i) {
// 获取单元格,如果单元格不存在则创建为空白单元格
Cell cell = row.getCell(i, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
String value;
// 将单元格内容转换为字符串
value = getCellValueAsString(cell);
rowMap.put(keys.get(i), value);
}
// 仅添加非空行(即所有值不全为空的行)
// 使用 stream() 和 allMatch() 检查所有值是否都为空字符串
if (!rowMap.values().stream().allMatch(String::isEmpty)) {
result.add(rowMap);
}
}
return result;
}
/**
* 辅助方法:将单元格内容转换为字符串。
* 处理不同类型的单元格数据。
*
* @param cell 单元格对象。
* @return 单元格内容的字符串表示。
*/
private static String getCellValueAsString(Cell cell) {
if (cell == null) {
return "";
}
CellType cellType = cell.getCellType();
switch (cellType) {
case STRING:
return cell.getStringCellValue().trim();
case NUMERIC:
// 对于日期类型,需要特殊处理
if (DateUtil.isCellDateFormatted(cell)) {
return cell.getDateCellValue().toString(); // 或者使用 SimpleDateFormat 格式化
} else {
// 对于数字,避免科学计数法,转换为普通字符串
DataFormatter formatter = new DataFormatter();
return formatter.formatCellValue(cell);
}
case BOOLEAN:
return String.valueOf(cell.getBooleanCellValue());
case FORMULA:
// 尝试评估公式结果
try {
return String.valueOf(cell.getNumericCellValue()); // 假设公式结果是数字
} catch (IllegalStateException e) {
return cell.getStringCellValue().trim(); // 否则尝试作为字符串
}
case BLANK:
return "";
default:
return cell.toString().trim();
}
}
// 示例用法 (假设您有一个 Workbook 对象)
public static void main(String[] args) throws Exception {
// 这是一个伪代码示例,实际使用需要加载 Excel 文件
// Workbook workbook = new XSSFWorkbook(new FileInputStream("your_excel_file.xlsx"));
// Sheet sheet = workbook.getSheetAt(0);
// List<Map<String, String>> orderedData = readExcelSheetOrdered(sheet);
// 模拟一个 Sheet 对象进行测试
// 假设 sheet 已经包含数据
// ... (省略创建模拟 Sheet 的复杂代码,实际项目中通过文件读取)
// 例如:
// Sheet mockSheet = new XSSFWorkbook().createSheet("Test Sheet");
// Row headerRow = mockSheet.createRow(0);
// headerRow.createCell(0).setCellValue("column1");
// headerRow.createCell(1).setCellValue("column2");
//
// Row dataRow1 = mockSheet.createRow(1);
// dataRow1.createCell(0).setCellValue("value1");
// dataRow1.createCell(1).setCellValue("value2");
//
// Row dataRow2 = mockSheet.createRow(2);
// dataRow2.createCell(0).setCellValue("value3");
// dataRow2.createCell(1).setCellValue("value4");
//
// List<Map<String, String>> orderedData = readExcelSheetOrdered(mockSheet);
// orderedData.forEach(map -> {
// map.entrySet().forEach(entry -> System.out.println(entry.getKey() + " -> " + entry.getValue()));
// System.out.println("---");
// });
// workbook.close();
}
}代码改动点说明:
通过将 HashMap 替换为 LinkedHashMap,可以有效地解决在 Java 中读取 Excel 数据时列顺序混乱的问题。LinkedHashMap 保证了元素的插入顺序,使得从 Excel 读取的数据能够精确地反映源文件的列布局。这对于后续的数据处理、数据验证以及将数据回写到 Excel 等操作至关重要,确保了数据流的完整性和可预测性。结合健壮的单元格类型处理,您可以构建一个可靠且高效的 Excel 数据读取模块。
以上就是Java 读取 Excel 数据时保持列顺序的策略的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号