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);

posted @ 2020-07-07 20:43  MR饶  阅读(9465)  评论(0编辑  收藏  举报