POI遍历行所有单元格的两种方式,getPhysicalNumberOfCells方式有问题,勿用

news2025/1/19 20:26:27

今天看POI源码的时候,发现HSSFWorkbook类型的工作簿,行数据是用TreeMap<Integer, HSSFRow>存储的,列数据是用HSSFCell[]数组来存的;XSSFWorkbook类型的工作簿,行数据是用SortedMap<Integer, XSSFRow>存储的,列数据是用TreeMap<Integer, XSSFCell>来存的。其中数组方式的代码中有初始容量还有扩容操作(直接理解为list集合就行),也就意味着里面的数组可能存在为null的数据(单元格)。

下面我们用HSSFWorkbook类型的工作簿来分析问题

getPhysicalNumberOfCells遍历可能导致空指针等问题

看到getPhysicalNumberOfCells的时候,感觉好像在项目代码里看到过,就搜索了一下,发现有人使用了这个方法,然后来遍历从Row对象里面获取Cell单元格。而且我一年多前应该也用过这个方法,可能是报错了就改了其他方法,只是当时没有去分析为什么报错。
直接说结论,使用getPhysicalNumberOfCells的结果来遍历,可能会导致空指针问题,下面截图是HSSFWorkbook类型的工作簿Row行对象获取单元格物理数量的代码。
在这里插入图片描述
假设有一个excel,三个单元格位置如下图所示
在这里插入图片描述
Row对象里面的cells数组中,应该是[1, 1, null, null, null, 1, null, null,,,,]的分布(这里不够严谨,实际数组里面的是单元格对象,还有最后为null的是因为有初始化容量和扩容机制)。getPhysicalNumberOfCells拿到的单元格数量就是3,所以当用fori遍历的时候,一旦列单元格不是连续的是可能会导致空指针的。我们这NPE因为我们第三个单元格是空的。
用一段代码演示一下:

public static void main(String[] args) {
        // 创建一个工作簿 这里就不管关闭操作
        final Workbook workbook = new HSSFWorkbook();
        // 创建一个sheet
        final Sheet sheet = workbook.createSheet();
        // 创建第一行对象
        final Row firstRow = sheet.createRow(0);
        // 创建三个单元格,并设置值
        final Cell cell_zero = firstRow.createCell(0);
        final Cell cell_one = firstRow.createCell(1);
        final Cell cell_five = firstRow.createCell(5);
        cell_zero.setCellValue("1");
        cell_one.setCellValue("1");
        cell_five.setCellValue("1");
        // 这里拿到的单元格数量是3
        final int physicalNumberOfCells = firstRow.getPhysicalNumberOfCells();
        System.out.println("单元格物理数量:" + physicalNumberOfCells);
        // 遍历的时候,i = 2就会发生NPE,因为firstRow.getCell(2) = null
        for (int i = 0; i < physicalNumberOfCells; i++) {
            System.out.println(i + ": " + firstRow.getCell(i).getStringCellValue());
        }
    }

运行截图:
在这里插入图片描述
如果没有报错,那就恰好你那张表指定行里面的列单元格都是连续的,中间没有为空的单元格。虽然这种情况不会报错,但是建议不要使用,如果非要使用,最好判断一下获取的单元格是否为null
除了NPE问题,还有可能会出现单元格没被遍历到问题,比如我们加了判断操作,为空就跳过本次循环,最后会发现,F列的单元格是没有被遍历到的。

第一种方式,使用迭代器获取所有单元格

Row对象提供了一个cellIterator方法,通过这个方法,可以拿到一个包含当前行所有单元格对象的迭代器。这种方式是最推荐的。

    public static void main(String[] args) {
        // 创建一个工作簿 这里就不管关闭操作
        final Workbook workbook = new HSSFWorkbook();
        // 创建一个sheet
        final Sheet sheet = workbook.createSheet();
        // 创建第一行对象
        final Row firstRow = sheet.createRow(0);
        // 创建三个单元格,并设置值
        final Cell cell_zero = firstRow.createCell(0);
        final Cell cell_one = firstRow.createCell(1);
        final Cell cell_five = firstRow.createCell(5);
        cell_zero.setCellValue("1");
        cell_one.setCellValue("1");
        cell_five.setCellValue("1");

        // 拿到单元格迭代器
        Iterator<Cell> cellIterator = firstRow.cellIterator();
        // 遍历每个单元格,cell一定不为null
        cellIterator.forEachRemaining(cell -> {
            System.out.println(cell.getColumnIndex() + ": " + cell.getStringCellValue());
        });
    }

正常输出,不会有NPE问题
在这里插入图片描述

第二种方式,获取起始列和最后一列然后遍历

通过Row对象的getFirstCellNumgetLastCellNum方法,就能拿到这行第一个单元格的下标,还有最后一个单元格后一个单元格的下标。所以遍历的时候,一定要注意getLastCellNum拿到的值,还有因为中间可能存在空的单元格,所以也要判断拿到的单元格是否为null。虽然这种方式也可行,但是还是推荐第一种迭代器的方式。

    public static void main(String[] args) {
        // 创建一个工作簿 这里就不管关闭操作
        final Workbook workbook = new HSSFWorkbook();
        // 创建一个sheet
        final Sheet sheet = workbook.createSheet();
        // 创建第一行对象
        final Row firstRow = sheet.createRow(0);
        // 创建三个单元格,并设置值
        final Cell cell_zero = firstRow.createCell(0);
        final Cell cell_one = firstRow.createCell(1);
        final Cell cell_five = firstRow.createCell(5);
        cell_zero.setCellValue("1");
        cell_one.setCellValue("1");
        cell_five.setCellValue("1");

        // 拿到起始的列索引,比如我们0列就有数据,那就是1
        short firstCellNum = firstRow.getFirstCellNum();
        // 拿到最后的列索引,这个要注意,比如我们最后一列下表是5,那这个拿到的就是6
        short lastCellNum = firstRow.getLastCellNum();
        System.out.println("第一单元格index:" + firstCellNum);
        System.out.println("最后一各单元格后一格index:" + lastCellNum);
        // 注意是 i < lastCellNum
        for (int i = firstCellNum; i < lastCellNum; i++) {
            // getCell也要判断是否为null,不然也有可能出现NPE问题
            System.out.println(i + " :" + firstRow.getCell(i));
        }
    }

运行截图:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2255920.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NUMA-非统一内存访问架构

NUMA&#xff08;Non-Uniform Memory Access&#xff09; 是一种计算机内存架构&#xff0c;主要用于多处理器系统。NUMA架构中的每个处理器都连接到自己的本地内存&#xff0c;并且可以访问其他处理器的内存&#xff0c;但访问其他处理器的内存速度较慢。 内核通过调度优化进…

WPF+LibVLC开发播放器-LibVLC在C#中的使用

LibVLC在C#中的使用 安装包Nuget使用控件使用播放器初始化加载视频文件 视频教程&#xff1a; 使用WPFLibVLC快速开发一个播放器 安装包Nuget 安装下面两个包,必须安装两个 一个是相关框架对应的包&#xff0c;Winform就安装LibVLCSharp.Winform;WPF就安装LibVLCSharp.WPF&am…

用GPT零负担学单片机之点亮一颗cpu 第3节 训练or特征匹配?用GPT开发嵌入式

用GPT零负担学单片机之点亮一颗cpu 第3节 训练or特征匹配&#xff1f;AI写代码 大家好,我是小杰学长 如果你是大学生 遇到电子技术 学习 成长 入行难题 我曾经通过大学比赛赚钱 从事嵌入式AI 航天军工 用特别的学习和求职方法线下半年带50学弟学妹入行开发 主页佳喔威信&…

基于Java Springboot在线招聘APP且微信小程序

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 微信…

动力商城-05 阿里云短信服务

1.添加依赖 <dependency><groupId>com.aliyun</groupId><artifactId>dysmsapi20170525</artifactId><version>2.0.24</version></dependency>2.控制层 Api(tags "短信业务接口管理") RequestMapping("p/sms&…

深入解析 HTML Input 元素:构建交互性表单的核心

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

HAMR技术进入云存储市场!

2024年12月3日&#xff0c;Seagate宣布其Mozaic 3系列HAMR&#xff08;热辅助磁记录&#xff09;硬盘获得了来自一家领先云服务提供商&#xff08;可能AWS、Azure或Google Cloud其中之一&#xff09;以及其他高容量硬盘客户的资格认证。 Seagate的Mozaic 3技术通过引入热辅助磁…

图数据库 | 12、图数据库架构设计——高性能计算架构

在传统类型的数据库架构设计中&#xff0c;通常不会单独介绍计算架构&#xff0c;一切都围绕存储引擎展开&#xff0c;毕竟存储架构是基础&#xff0c;尤其是在传统的基于磁盘存储的数据库架构设计中。 类似地&#xff0c;在图数据库架构设计中&#xff0c;项目就围绕存储的方…

【工具变量】地级市城市全社会用电量数据(2006-2021年)

一、数据范围&#xff1a;覆盖中国300多个地级市 二、包含指标&#xff1a; 省份、地级市、年份、全社会用电量。 三、数据来源&#xff1a;国家电网查询数据。对于极大部分城市&#xff0c;国网售电量就是全社会用电量(往年的售电量和全社会用电量数据相同&#xff09;,此外…

请求响应:常见参数接收及封装(数组集合参数及日期参数)

数组参数 在前端页面的表单中&#xff0c;存在复选框元素&#xff0c;当提交表单到后端的时候&#xff0c;会将复选框中的全部内容提交到后端进行处理&#xff0c;由于复选框中往往存在很多数据&#xff0c;并且同复选框中数据名称相同&#xff0c;这样的请求参数叫做数组参数…

兔子的寿命有多长?

在宠物的世界里&#xff0c;兔子以其灵动的身姿、柔软的皮毛和温顺的性格深受人们喜爱。然而&#xff0c;当我们满心欢喜地将兔子迎进家门时&#xff0c;可曾想过它们能陪伴我们多久&#xff1f;兔子的寿命&#xff0c;是一个值得深入探讨的话题&#xff0c;它不仅关乎生命的时…

本地多卡(3090)部署通义千问Qwen-72B大模型提速实践:从龟速到够用

最近在做文本风格转化&#xff0c;涉及千万token级别的文本。想用大模型转写&#xff0c;在线的模型一来涉及数据隐私&#xff0c;二来又不想先垫钱再找报销。本地的7-9B小模型又感觉效果有限&#xff0c;正好实验室给俺配了4卡3090的机子&#xff0c;反正也就是做个推理&#…

鸿蒙开发——键值型数据库的基本使用与跨设备同步

1、简 述 ❓ 什么是键值型数据库 键值型数据库&#xff08;KV-Store&#xff09;是一种非关系型数据库&#xff0c;其数据以“键值”对的形式进行组织、索引和存储&#xff0c;其中“键”作为唯一标识符。 键值型数据库适合很少数据关系和业务关系的业务数据存储。 另外&#…

STM32一keil5更换芯片后报错问题的解决。

目录 一、STM32型号认识二、报错问题三、常用的启动配置文件四、问题解决 一、STM32型号认识 二、报错问题 当我们在原来工程下修改芯片时&#xff0c;原本可以编译通过的代码突然很多报错。如下所示&#xff0c;这是因为我们的启动文件配置错误。对于不同型号的芯片其flash容量…

CentOS安装Nginx并配置为系统服务

前言 Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器 [13]&#xff0c;同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔赛索耶夫为俄罗斯访问量第二的Rambler.ru站点&#xff08;俄文&#xff1a;Рамблер&#xff09;开发的&#xff0c;公开版本1.19.6发布…

部署loki,grafana 以及springcloud用法举例

文章目录 场景docker 部署grafanadocker-compose部署loki维护配置文件 local-config.yaml维护docker-compose.yml配置启动 grafana 添加loki数据源springcloud用法举例查看loki的explore,查看日志 场景 小公司缺少运维岗位&#xff0c;需要研发自己部署日志系统&#xff0c;elk…

快速学习selenium基础操作

全篇大概19000字&#xff08;含代码&#xff09;&#xff0c;建议阅读时间1h 什么是Selenium&#xff1f; Selenium是一系列自动化工具集的统称&#xff0c;官方工具有 Selenium IDE、Selenium WebDriver、Selenium Grid&#xff0c; 主要用于桌面端Web应用程序的自动化。能够通…

使用uniapp开发小程序场景:在百度地图上调用接口返回的设备相关信息并展示

首先在百度地图开发者平台注册微信小程序开发密钥下载百度地图SDK-bmap-wx.min.js,下载地址在项目入口index.html页面进行引入页面中进行调用&#xff0c;代码示例如下<map id"map" longitude"108.95" latitude"34.34" scale"3" :m…

SPI驱动模型框架及spidev.c分析---学习记录

目录 SPI设备如何使用 SPI驱动模型框架 SPI 控制器/SPI Master分析 SPI 设备端/SPI Slave分析 SPI 控制器/SPI Master与SPI 设备端/SPI Slave驱动模型 Linux内核自带的SPI 设备端/SPI Slave代码spidev.c 小结 SPI设备如何使用 一般我们使用spi设备驱动是类似文件操作&#xff0…

WordPress XStore Elementor 前端与编辑器内容不同步的问题

最近在新站更换成XStore的Elementor模板后&#xff0c;在编辑器修改完的内容前端网页部分没有同步&#xff0c;一开始清除了缓存没有解决。后面尝试重新安装也还是存在这个问题。 后续又在服务器上删除了Elementor插件缓存文件&#xff0c;问题依然存在。 最后通过在Elemento…