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

MR饒發表於2020-07-07

一:迴圈遍歷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);

相關文章