­

Lambda 表達式遍歷集合時用remove方法刪除list集合中滿足條件的元素問題

一:循環遍歷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);