0

0

读取Excel数据并保持列顺序的Java实践

聖光之護

聖光之護

发布时间:2025-07-21 21:26:01

|

448人浏览过

|

来源于php中文网

原创

读取Excel数据并保持列顺序的Java实践

本文旨在解决使用Java读取Excel数据并存储到List>时,Map中列顺序混乱的问题。核心解决方案是利用LinkedHashMap来替代默认的HashMap,从而确保数据在Map中保持与Excel源文件一致的插入顺序,便于后续处理或写回Excel。文章将提供详细的代码示例和解释,帮助开发者实现有序的Excel数据处理。

1. 问题背景:HashMap的无序性

在java中,java.util.hashmap是一种常用的键值对存储结构。然而,hashmap的内部实现是基于哈希表,它不保证元素的迭代顺序。这意味着当你将excel表格中的列名和值存入hashmap时,即使你按照从左到右的顺序插入,hashmap在迭代时也可能以任意顺序返回这些键值对。这对于需要严格保持列顺序的场景(如将数据写回excel或按原顺序处理数据)来说,是一个显著的问题。

例如,一个Excel表格的列顺序是 column1, column2:

column1    column2
value1      value2
value3      value4

如果使用HashMap存储,得到的Map可能呈现如下无序状态:

0 = "column2" -> value2
    "column1" -> value1
1 = "column2" -> value4
    "column1" -> value3

这与我们期望的 column1 -> value1, column2 -> value2 的顺序不符。

2. 解决方案:使用LinkedHashMap保持插入顺序

为了解决HashMap的无序性问题,Java提供了java.util.LinkedHashMap。LinkedHashMap继承自HashMap,并额外维护了一个双向链表,用于记录元素的插入顺序。因此,当你遍历LinkedHashMap时,它会按照键值对被插入的顺序返回它们。这正是我们读取Excel数据并希望保持列顺序所需的特性。

立即学习Java免费学习笔记(深入)”;

Autoppt
Autoppt

Autoppt:打造高效与精美PPT的AI工具

下载

3. 代码实现与优化

以下是修改后的readExcelSheet方法,它将HashMap替换为LinkedHashMap,以确保列的顺序得到保留。

import org.apache.poi.ss.usermodel.*;
import java.util.*;

public class ExcelReaderUtil {

    /**
     * 从Excel工作表中读取数据,并以有序的List>形式返回。
     * 每个Map代表一行数据,Map中的键值对顺序与Excel列的插入顺序一致。
     *
     * @param sheet 要读取的Excel工作表对象
     * @return 包含Excel数据的List,如果工作表为空则返回空列表
     */
    public static List> readExcelSheet(Sheet sheet) {
        // 获取行的迭代器
        Iterator rows = sheet.iterator();

        // 如果没有行,则返回空列表
        if (!rows.hasNext()) {
            return Collections.emptyList();
        }

        // 读取表头(第一行)作为Map的键
        Row header = rows.next();
        List keys = new ArrayList<>();
        // 遍历表头单元格,获取列名
        for (Cell cell : header) {
            String value = getCellValueAsString(cell); // 使用辅助方法获取单元格值
            if (!value.isEmpty()) {
                keys.add(value);
            } else {
                // 遇到空列名时,可以根据实际需求选择跳出或继续
                // 这里选择跳出,认为后续列可能不再是有效表头
                break;
            }
        }

        // 初始化结果列表
        List> result = new ArrayList<>();

        // 遍历剩余的每一行数据
        while (rows.hasNext()) {
            Row row = rows.next();
            // 使用LinkedHashMap来保证列的插入顺序
            Map rowMap = new LinkedHashMap<>();

            // 遍历表头键,按顺序填充当前行的数据
            for (int i = 0; i < keys.size(); ++i) {
                // 获取单元格,如果不存在则创建为空白单元格
                Cell cell = row.getCell(i, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
                String value = getCellValueAsString(cell); // 使用辅助方法获取单元格值
                rowMap.put(keys.get(i), value);
            }

            // 只有当行不为空时才添加到结果列表
            // 判断行是否为空:检查Map中所有值是否都为空字符串
            if (!rowMap.values().stream().allMatch(String::isEmpty)) {
                result.add(rowMap);
            }
        }

        return result;
    }

    /**
     * 辅助方法:安全地获取单元格的字符串值,处理不同类型的单元格。
     *
     * @param cell 单元格对象
     * @return 单元格的字符串表示,如果单元格为null或空白,则返回空字符串
     */
    private static String getCellValueAsString(Cell cell) {
        if (cell == null) {
            return "";
        }
        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                // 对于日期类型,需要额外处理,这里简化为数值
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue().toString(); // 或者格式化为特定日期字符串
                } else {
                    return String.valueOf(cell.getNumericCellValue());
                }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                // 对于公式单元格,可以尝试获取计算后的值
                try {
                    return String.valueOf(cell.getNumericCellValue()); // 尝试获取数值结果
                } catch (IllegalStateException e) {
                    try {
                        return cell.getStringCellValue(); // 尝试获取字符串结果
                    } catch (IllegalStateException ex) {
                        return ""; // 无法获取值
                    }
                }
            case BLANK:
                return "";
            default:
                return ""; // 默认返回空字符串
        }
    }

    // 示例用法 (需要Apache POI库)
    public static void main(String[] args) throws Exception {
        // 假设有一个名为 "example.xlsx" 的Excel文件
        // 创建一个模拟的Workbook和Sheet用于测试
        Workbook workbook = new org.apache.poi.xssf.usermodel.XSSFWorkbook();
        Sheet sheet = workbook.createSheet("Sheet1");

        // 创建表头
        Row headerRow = sheet.createRow(0);
        headerRow.createCell(0).setCellValue("column1");
        headerRow.createCell(1).setCellValue("column2");
        headerRow.createCell(2).setCellValue("column3"); // 增加一列测试

        // 创建数据行1
        Row dataRow1 = sheet.createRow(1);
        dataRow1.createCell(0).setCellValue("value1");
        dataRow1.createCell(1).setCellValue("value2");
        dataRow1.createCell(2).setCellValue(123); // 测试数值类型

        // 创建数据行2
        Row dataRow2 = sheet.createRow(2);
        dataRow2.createCell(0).setCellValue("value3");
        dataRow2.createCell(1).setCellValue("value4");
        dataRow2.createCell(2).setCellValue(true); // 测试布尔类型

        // 创建空行(应被过滤)
        sheet.createRow(3);

        // 调用读取方法
        List> data = readExcelSheet(sheet);

        // 打印结果,观察列顺序
        for (Map rowMap : data) {
            System.out.println("--- Row ---");
            rowMap.forEach((key, value) -> System.out.println("  " + key + " -> " + value));
        }

        workbook.close();
    }
}

代码改进说明:

  1. LinkedHashMap的使用: 最核心的改动是将 Map rowMap = new HashMap(); 替换为 Map rowMap = new LinkedHashMap();。这确保了在将列名和值放入rowMap时,它们会按照插入的顺序(即Excel中从左到右的列顺序)进行存储。
  2. getCellValueAsString辅助方法: 原始代码中直接使用 cell.toString() 来获取单元格值,这可能导致非字符串类型的单元格(如数值、日期、布尔值)在转换时出现问题或不符合预期。新增的getCellValueAsString辅助方法根据单元格类型安全地获取其字符串表示,提高了代码的健壮性。
  3. 空行判断优化: if (!rowMap.values().stream().allMatch(String::isEmpty)) 能够有效过滤掉所有单元格都为空的行。

4. 其他Map类型选择

除了LinkedHashMap,Java还提供了其他Map实现,它们在特定场景下也可能有用:

  • TreeMap: TreeMap实现了SortedMap接口,它会根据键的自然顺序(或自定义的比较器)对键进行排序。如果你希望Excel列数据按照列名的字母顺序(或数字顺序)而不是原始插入顺序进行存储,那么TreeMap可能是一个选择。然而,对于保留原始Excel列顺序的需求,LinkedHashMap是更直接和合适的方案。

5. 注意事项与总结

  • 依赖管理: 上述代码使用了Apache POI库来处理Excel文件。请确保你的项目中已添加相应的Maven或Gradle依赖,例如:
    
    
        org.apache.poi
        poi
        5.2.3 
    
    
        org.apache.poi
        poi-ooxml
        5.2.3 
    
  • 单元格类型处理: getCellValueAsString方法提供了一个基本的单元格类型处理示例。在实际应用中,你可能需要更精细地处理日期格式、公式求值错误等情况,以满足具体的业务需求。
  • 性能考量: 对于非常大的Excel文件,一次性将所有数据加载到内存中的List可能会消耗大量内存。在这种情况下,可以考虑流式处理数据,或者分批处理。
  • 空列名处理: 示例代码在遇到空列名时会break跳出表头读取。如果你的Excel文件可能存在中间有空列名但后续仍有有效列名的情况,你可能需要调整此逻辑,例如继续读取所有单元格直到行尾。

通过将HashMap替换为LinkedHashMap,可以有效地解决在Java中读取Excel数据时列顺序混乱的问题,确保数据在内存中保持与源文件一致的结构,从而简化后续的数据处理和回写操作。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

836

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

741

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

736

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

399

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16926

2023.08.03

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

72

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
RunnerGo从入门到精通
RunnerGo从入门到精通

共22课时 | 1.7万人学习

尚学堂Mahout视频教程
尚学堂Mahout视频教程

共18课时 | 3.2万人学习

Linux优化视频教程
Linux优化视频教程

共14课时 | 3.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号