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

今天看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();// 创建一个sheetfinal 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");// 这里拿到的单元格数量是3final int physicalNumberOfCells = firstRow.getPhysicalNumberOfCells();System.out.println("单元格物理数量:" + physicalNumberOfCells);// 遍历的时候,i = 2就会发生NPE,因为firstRow.getCell(2) = nullfor (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();// 创建一个sheetfinal 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一定不为nullcellIterator.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();// 创建一个sheetfinal 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列就有数据,那就是1short firstCellNum = firstRow.getFirstCellNum();// 拿到最后的列索引,这个要注意,比如我们最后一列下表是5,那这个拿到的就是6short lastCellNum = firstRow.getLastCellNum();System.out.println("第一单元格index:" + firstCellNum);System.out.println("最后一各单元格后一格index:" + lastCellNum);// 注意是 i < lastCellNumfor (int i = firstCellNum; i < lastCellNum; i++) {// getCell也要判断是否为null,不然也有可能出现NPE问题System.out.println(i + " :" + firstRow.getCell(i));}}

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

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

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

相关文章

DataSophon集成CMAK KafkaManager

本次集成基于DDP1.2.1 集成CMAK-3.0.0.6 设计的json和tar包我放网盘了. 通过网盘分享的文件&#xff1a;DDP集成CMAK 链接: https://pan.baidu.com/s/1BR70Ajj9FxvjBlsOX4Ivhw?pwdcpmc 提取码: cpmc CMAK github上提供了zip压缩包.将压缩包解压之后 在根目录下加入启动脚本…

ArrayList常见操作源码逐句剖析

目录 前言 正文 1.需要了解的一些字段属性 1.存储 ArrayList 元素的数组缓冲区。 2.集合的大小 3.默认集合容量大小 2.ArrayList对象创建 1.无参构造 2.有参构造1 3.有参构造2 3.添加元素add(E e)以及扩容机制 ​编辑 后言 前言 源码的剖析有助于理解设计模式&…

重磅更新:CnosDB 2.3.5.4 版本上线, 性能提升,问题修复一网打尽

&#x1f4e2; 重磅更新&#xff1a;CnosDB 2.3.5.4 版本上线, 性能提升&#xff0c;问题修复一网打尽 &#x1f4e2; 我们很高兴地向大家介绍最新版本的更新&#xff0c;以下是本次更新的主要内容&#xff1a; &#x1f539; 版本号&#xff1a;2.3.5.4 &#x1f539; 发布…

SD-WAN 2.0 在金融行业的典型应用场景

目录 全扁平化组网 场景需求 应用方案 SD-WAN 2.0 在金融行业的创新实践 SD-WAN5G提高金融行业网络接入可靠性 全扁平化组网 随着金融机构数字化转型的推进&#xff0c;机构业务的多样性、复杂性、 个性化等要求&#xff0c;对现有的金融机构网络架构与网管人员运维模式提出…

如何延长相机电池续航时间

如果你曾在拍摄过程中突然发现相机电池电量不足&#xff0c;就会知道那有多让人紧张和沮丧了。无论你是在拍摄小朋友的生日派对、家庭聚会&#xff0c;还是作为一名专业摄影师在工作&#xff0c;保持电池有电都是至关重要的。否则&#xff0c;你就有可能错过精彩瞬间&#xff0…

C#开发-集合使用和技巧(十)Union用法-并集

在 C# 中&#xff0c;IEnumerable 的 Union 方法用于返回两个序列的并集。Union 方法会去除重复的元素&#xff0c;确保结果集中每个元素都是唯一的。以下是 Union 方法的基本用法&#xff1a; 基本语法 public static IEnumerable<TSource> Union<TSource>(this…

轻量化特征融合 | YOLOv11 引入一种基于增强层间特征相关性的轻量级特征融合网络 | 北理工新作

本改进已同步到Magic框架 摘要—无人机图像中的小目标检测由于分辨率低和背景融合等因素具有挑战性,导致特征信息有限。多尺度特征融合可以通过捕获不同尺度的信息来增强检测,但传统策略效果不佳。简单的连接或加法操作无法充分利用多尺度融合的优势,导致特征之间的相关性不…

ABAP 系统变量SY-INDEX与SY-TABIX的区别

ABAP系统变量SY-INDEX与SY-TABIX都是在循环中使用&#xff1a; SY-INDEX在Do...EndDo和While...EndWhile中起作用&#xff1b; SY-TABIX在Loop...EndLoop中有效。 详见如下实例&#xff1a; REPORT ztest_index_tabix.DATA:lit_vbak TYPE STANDARD TABLE OF vbak,lwa_vbak …

方案拆解 | 打击矩阵新规频出!2025矩阵营销该怎么玩?

社媒平台的矩阵营销又要“变天”了&#xff1f;&#xff01; 11月18日&#xff0c;小红书官方发表了被安全薯 称为“小红书史上最严打击黑灰产专项”新规&#xff0c;其中就包括黑灰产矩阵号的公告。 ▲ 图源&#xff1a;小红书 实际上&#xff0c;不包括这次&#xff0c;今年…

Lua语言入门 - Lua 数组

Lua 数组 数组&#xff0c;就是相同数据类型的元素按一定顺序排列的集合&#xff0c;可以是一维数组和多维数组。 在 Lua 中&#xff0c;数组不是一种特定的数据类型&#xff0c;而是一种用来存储一组值的数据结构。 实际上&#xff0c;Lua 中并没有专门的数组类型&#xff…

SVM的基本思想

一、SVM的基本思想 SVM的基本思想是在样本的向量空间中寻找一个超平面&#xff0c;使得两类样本被分割在平面的两端。这样的平面理论上有无穷多个&#xff0c;但SVM的目标是找到一个最优的超平面&#xff0c;即两侧距离超平面最近的样本点到超平面的距离被最大化的超平面。这个…

Java基于SpringBoot的网上订餐系统,附源码

博主介绍&#xff1a;✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&…

【 工具变量】IPCC碳排放因子数据测算表

一、数据简介&#xff1a; 排放因子法是IPCC提出的一种碳排放估算方法&#xff0c;也是目前适用范围最广、应用最为普遍的方法。将各类能源消耗的实物统计量转变为标准统计量&#xff0c;再乘以各自的碳排放因子&#xff0c;加总之后就可以得到碳排放总量。如果按照ISO14064标…

备忘录模式的理解和实践

引言 在软件开发中&#xff0c;我们经常会遇到需要保存对象状态并在某个时间点恢复到该状态的需求。这种需求类似于我们平时说的“后悔药”&#xff0c;即允许用户撤销之前的操作&#xff0c;恢复到某个之前的状态。备忘录模式&#xff08;Memento Pattern&#xff09;正是为了…

湖南铂乐家具新潮流,岛台不再是大平层的专属

湖南铂乐家具设计师们以巧思打破常规&#xff0c;无论是精致温馨的小户型公寓&#xff0c;还是布局紧凑的普通住宅&#xff0c;都能找到适配的岛台设计。以往岛台总是与宽敞开阔的大平层空间紧密相连&#xff0c;仿佛是大户型的身份象征。而如今岛台不再是大平层的专属。 在固…

RK3568笔记3:开发板启动流程

第1章 启动流程 1.1 上电复位 CPU 复位&#xff0c;进入启动模式。系统硬件查找启动设备&#xff08;如 eMMC&#xff09;。 1.2 ROM Code 阶段&#xff08;硬件引导&#xff09; 在片上 ROM 中存储的启动代码&#xff08;BootROM&#xff09;运行。ROM Code 从 eMMC 的 Boo…

重邮+数字信号处理实验三:z变换及离散LTI系统的z域分析

实验目的&#xff1a; &#xff08; 1 &#xff09;学会运用 Matlab 求离散时间信号的有理函数 z 变换的部分分式展开&#xff1b; &#xff08; 2 &#xff09;学会运用 Matlab 分析离散时间系统的系统函数的零极点&#xff1b; &#xff08; 3 &#xff09;学会运用 …

dolphinScheduler 任务调度

#Using docker-compose to Start Server #下载&#xff1a;https://dlcdn.apache.org/dolphinscheduler/3.1.9/apache-dolphinscheduler-3.1.9-src.tar.gz $ DOLPHINSCHEDULER_VERSION3.1.9 $ tar -zxf apache-dolphinscheduler-"${DOLPHINSCHEDULER_VERSION}"-src.t…

node.js中跨域请求有几种实现方法

默认情况下&#xff0c;出于安全考虑&#xff0c;浏览器会实施同源策略&#xff0c;阻止网页向不同源的服务器发送请求或接收来自不同源的响应。 同源策略&#xff1a;协议、域名、端口三者必须保持一致 <!DOCTYPE html> <html lang"en"> <head>&l…

【机器学习】机器学习的基本分类-监督学习-决策树(Decision Tree)

决策树是一种树形结构的机器学习模型&#xff0c;适用于分类和回归任务。它通过一系列基于特征的条件判断来将数据分割为多个子区域&#xff0c;从而预测目标变量的值。 1. 决策树的结构 根节点&#xff08;Root Node&#xff09; 决策树的起点&#xff0c;包含所有样本。根据某…