Lambda 表達式遍歷集合時用remove方法刪除list集合中滿足條件的元素問題
- 2020 年 7 月 7 日
- 筆記
一:循環遍歷list集合的四種方式
-
簡單for循環
-
iterator循環
-
增加for循環
-
Lambda表達式
二:四種遍歷方式的用法示例
//簡單for循環
List<SalaryAdjustmentFile> fileList = new ArrayList<>();
fileList.add(new SalaryAdjustmentFile());
//此處省略加入list元素
for(int i = 0; n < fileList.size(); i++){
…..//此處省略具體實現方法
}
//iterator循環
Iterator<SalaryAdjustmentFile> iter = fileList.iterator();
while(iter.hasNext()){
…..//此處省略具體實現方法
}
// 增強for循環
for(SalaryAdjustmentFile salaryAdjustmentFile : fileList){
…..//此處省略具體實現方法
}
//Lambda表達式
fileList.stream().forEach(salaryAdjustmentFile -> {
…..//此處省略具體實現方法
});
三:刪除集合元素
因為自己代碼中是使用Lambda表達式實現的list集合遍歷,所以此處只展示這種方式遍歷集合刪除元素的功能
fileList.stream().forEach(salaryAdjustmentFile -> {
String staffId = salaryAdjustmentFile.getStaffId();
String modelFieldName = salaryAdjustmentFile.getModelFieldName();
String tenantId = BaseContextHandler.getTenantId();
String salaryPlanId = salaryAdjustmentFile.getSalaryPlanId();
SalaryAdjustmentFile last = salaryAdjustmentFileMapper.selectLastTimeFile(staffId,modelFieldName,salaryPlanId,tenantId);
Map map = salaryFileMapper.selectItemValueByStaffId(staffId,modelFieldName,salaryPlanId,tenantId);
if(StringUtils.isEmpty(last) && !map.isEmpty()){
salaryAdjustmentFile.setValueBeforeAdjustment((BigDecimal) map.get(“itemValue”));
}else {
salaryAdjustmentFile.setValueBeforeAdjustment(last.getValueAfterAdjustment());
}
salaryAdjustmentFile.creat(idWorker.nextStringId());
//如果滿足下麵條件則刪除元素
if((salaryAdjustmentFile.getValueBeforeAdjustment().subtract(salaryAdjustmentFile.getValueAfterAdjustment()) == BigDecimal.ZERO)){
fileList.remove(salaryAdjustmentFile);
}
});
上面的代碼在運行的時候,並不會如我們期望的一樣刪除list元素成功,而是控制台會報錯java.lang.NullPointerException: null
報錯原因分析:
經過百度搜索了解到,這是並發修改異常錯誤,是集合遍歷原理導致的,具體原因是這樣的:
不管是哪種方式的集合遍歷方法,當我們在遍歷某個集合的時候,Collection的實現並沒有同步化,如果在多線程應用程序中出現同時訪問,而且出現修改操作的時候都要求外部操作同步化;調用遍歷操作獲得的遍歷對象在多線程修改集合的時候也自動失效,並拋出java.util.ConcurrentModificationException。這種實現機制是fail-fast,對外部 的修改並不能提供任何保證。遍歷對象在被創建的時候,同時創建了一張單鏈的索引表,指針指向原始數據對象,只能順序讀取,不能逆向操作,而set、list等集合是動態、可變的數據結構;當原始對象改變時,索引並為改變,因此,索引指針繼續移動的時候,找不到要迭代的對象就會報錯。
四:針對第三步中錯誤的解決方案
將刪除對象放在一個臨時的集合中,最後執行removeAll方法移除,如下:
List<SalaryAdjustmentFile> removeList = new ArrayList<>();
fileList.stream().forEach(salaryAdjustmentFile -> {
String staffId = salaryAdjustmentFile.getStaffId();
String modelFieldName = salaryAdjustmentFile.getModelFieldName();
String tenantId = BaseContextHandler.getTenantId();
String salaryPlanId = salaryAdjustmentFile.getSalaryPlanId();
SalaryAdjustmentFile last = salaryAdjustmentFileMapper.selectLastTimeFile(staffId,modelFieldName,salaryPlanId,tenantId);
Map map = salaryFileMapper.selectItemValueByStaffId(staffId,modelFieldName,salaryPlanId,tenantId);
if(StringUtils.isEmpty(last) && !map.isEmpty()){
salaryAdjustmentFile.setValueBeforeAdjustment((BigDecimal) map.get(“itemValue”));
}else {
salaryAdjustmentFile.setValueBeforeAdjustment(last.getValueAfterAdjustment());
}
salaryAdjustmentFile.creat(idWorker.nextStringId());
//如果滿足下麵條件則刪除元素
if((salaryAdjustmentFile.getValueBeforeAdjustment().subtract(salaryAdjustmentFile.getValueAfterAdjustment()) == BigDecimal.ZERO)){
removeList.add(salaryAdjustmentFile);
}
});
//刪除掉集合中滿足刪除條件的數據
fileList.removeAll(removeList);