Java爬取数据写入MySQL时出现IndexOutOfBoundsException异常如何解决
异常原因
- 该
java.lang.IndexOutOfBoundsException为集合索引越界异常,报错显示索引2023超出长度为2023的集合的最大可访问索引(集合索引从0开始,长度为n的集合最大有效索引为n-1)。 - 触发场景为代码第101行的循环逻辑:循环以
atcId集合的长度为遍历上限,但其他字段对应的集合(如clrNm、rnum等)长度小于atcId,当循环索引超过短集合的最大索引时就会抛出异常。 - 导致各字段集合长度不一致的根源是数据解析逻辑错误:代码将接口请求放在了字段遍历的循环内,每提取一个字段就重新调用一次公开接口,若某次接口请求波动返回数据条数变化、或者某条数据缺失对应字段,就会出现各集合长度不匹配的问题,同时重复请求也会大幅降低运行效率、增加接口限流概率。
修复方案
第一步:调整数据解析逻辑,保证各字段集合长度一致
仅请求一次接口,拿到所有item节点后逐个提取对应字段,字段缺失时默认填充空字符串,从根源避免长度不一致问题,替换原字段遍历解析的全部代码:
// 仅调用一次接口获取完整返回数据 Document doc = null; try { doc = Jsoup.connect(url.toString()).get(); } catch (Exception e) { e.printStackTrace(); return; // 接口请求失败直接终止流程,避免后续空指针异常 } // 提取所有条目节点 Elements itemList = doc.select("item"); // 遍历每个条目一次性提取所有字段 for (Element item : itemList) { atcId.add(item.selectFirst("atcId") != null ? item.selectFirst("atcId").text() : ""); clrNm.add(item.selectFirst("clrNm") != null ? item.selectFirst("clrNm").text() : ""); depPlace.add(item.selectFirst("depPlace") != null ? item.selectFirst("depPlace").text() : ""); fdPrdtNm.add(item.selectFirst("fdPrdtNm") != null ? item.selectFirst("fdPrdtNm").text() : ""); fdSbjt.add(item.selectFirst("fdSbjt") != null ? item.selectFirst("fdSbjt").text() : ""); fdYmd.add(item.selectFirst("fdYmd") != null ? item.selectFirst("fdYmd").text() : ""); prdtClNm.add(item.selectFirst("prdtClNm") != null ? item.selectFirst("prdtClNm").text() : ""); rnum.add(item.selectFirst("rnum") != null ? item.selectFirst("rnum").text() : ""); }
第二步:优化数据库操作逻辑(可选,推荐)
原代码每次循环插入都新建数据库连接,性能极低,调整为批量插入模式,大幅提升入库效率:
Connection con = null; PreparedStatement pstmt = null; String driver = "com.mysql.cj.jdbc.Driver"; String dbUrl = "jdbc:mysql://localhost:3306/stuff?useSSL=false&characterEncoding=utf8"; String user = "root"; String pw = "rladydals2@"; String insertSql = "insert into lost_property(atcId, clrNm, depPlace, fdPrdtNm, fdSbjt, fdYmd, prdtClNm, rnum) values(?, ?, ?, ?, ?, ?, ?, ?)"; try { Class.forName(driver); con = DriverManager.getConnection(dbUrl, user, pw); con.setAutoCommit(false); // 开启事务批量提交 pstmt = con.prepareStatement(insertSql); for(int i=0;i<atcId.size();i++) { pstmt.setString(1, atcId.get(i)); pstmt.setString(2, clrNm.get(i)); pstmt.setString(3, depPlace.get(i)); pstmt.setString(4, fdPrdtNm.get(i)); pstmt.setString(5, fdSbjt.get(i)); pstmt.setString(6, fdYmd.get(i)); pstmt.setString(7, prdtClNm.get(i)); pstmt.setString(8, rnum.get(i)); pstmt.addBatch(); } // 批量执行插入 int[] executeRes = pstmt.executeBatch(); con.commit(); System.out.println("成功插入数据行数:" + executeRes.length); } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); // 插入失败回滚事务 try { if (con != null) con.rollback(); } catch (SQLException rollbackEx) { rollbackEx.printStackTrace(); } } finally { // 统一关闭资源 if (pstmt != null) { try { pstmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } }
内容的提问来源于stack exchange,提问作者YongMin
相关产品推荐
相关产品推荐

