目录

异常代码片段

详细说明

解决方案

使用迭代器进行遍历

使用临时集合存储结果


异常代码片段

if (ObjectUtil.isNotEmpty(candidateUsers)) {
    candidateUsers = candidateUsers.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList());
    for (String candidateUser : candidateUsers) {
        if (ObjectUtil.isNotEmpty(candidateUser) && candidateUser.contains("${")) {
            try {
                String expressionValue = expressionService.getStrValue(extendHisprocinst.getProcessInstanceId(), candidateUser);
                candidateUsers.add(expressionValue);
            } catch (Exception e) {
                log.error("解析人员报错,原因:{}", ExceptionUtil.stacktraceToString(e));
            }
        }
    }
}

这段代码中出现ConcurrentModificationException异常的原因是,在遍历candidateUsers集合的同时对其进行了修改。

具体来说,是在遍历过程中通过candidateUsers.add(expressionValue);向集合中添加元素,这会导致ConcurrentModificationException异常。

详细说明

详细解释

1.集合的遍历机制:

  • 当你使用for-each循环遍历集合时,实际上是在使用迭代器(Iterator)来遍历集合中的元素。

  • 迭代器在遍历集合时会维护一个内部计数器,以跟踪当前遍历的位置。

  • 每次调用next()方法时,迭代器会移动到下一个位置,并返回当前位置的元素。

2.并发修改检测:

  • Java集合框架中的许多集合类(如ArrayList)在遍历期间会检查集合是否被修改过。

  • 这种检查是通过一个称为modCount的字段来实现的,每当集合被修改时(例如添加或删除元素),modCount值就会增加。

  • 迭代器有一个expectedModCount字段,它在迭代器创建时被初始化为集合的modCount值。

  • 在每次调用next()方法时,迭代器会检查expectedModCount是否等于集合的当前modCount值。

  • 如果expectedModCount与当前modCount不匹配,说明集合在遍历过程中被修改过,这时迭代器会抛出ConcurrentModificationException异常。

代码分析

  • 在这段代码中,首先使用了流操作过滤并去重candidateUsers集合,然后遍历这个集合。
  • 在遍历过程中,如果满足某个条件(即candidateUser包含${),会尝试从expressionService获取表达式的值,并将其添加回candidateUsers集合中。
  • 这种做法违反了遍历过程中的并发修改规则,因此抛出了ConcurrentModificationException异常。

解决方案

为了避免这个问题,可以采用以下几种方法之一。

使用迭代器进行遍历

使用迭代器遍历集合,并在外部循环中处理元素,避免直接修改正在遍历的集合。

这种方法避免了直接修改正在遍历的集合,而是使用迭代器来安全地访问集合中的元素。

if (ObjectUtil.isNotEmpty(candidateUsers)) {
    candidateUsers = candidateUsers.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList());
    
    Iterator<String> iterator = candidateUsers.iterator();
    while (iterator.hasNext()) {
        String candidateUser = iterator.next();
        if (ObjectUtil.isNotEmpty(candidateUser) && candidateUser.contains("${")) {
            try {
                String expressionValue = expressionService.getStrValue(extendHisprocinst.getProcessInstanceId(), candidateUser);
                candidateUsers.add(expressionValue);
            } catch (Exception e) {
                log.error("解析人员报错,原因:{}", ExceptionUtil.stacktraceToString(e));
            }
        }
    }
}

使用临时集合存储结果

创建一个新的集合来存储处理后的结果,而不是直接修改原始集合。从而避免了ConcurrentModificationException异常。

选择其中一种方法即可解决问题。如果需要保留原始集合的内容,建议使用第二种方法(使用临时集合存储结果)。

if (ObjectUtil.isNotEmpty(candidateUsers)) {
    List<String> tempCandidateUsers = new ArrayList<>();
    candidateUsers = candidateUsers.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList());
    
    for (String candidateUser : candidateUsers) {
        if (ObjectUtil.isNotEmpty(candidateUser) && candidateUser.contains("${")) {
            try {
                String expressionValue = expressionService.getStrValue(extendHisprocinst.getProcessInstanceId(), candidateUser);
                tempCandidateUsers.add(expressionValue);
            } catch (Exception e) {
                log.error("解析人员报错,原因:{}", ExceptionUtil.stacktraceToString(e));
            }
        } else {
            tempCandidateUsers.add(candidateUser);
        }
    }
    
    candidateUsers = tempCandidateUsers;
}
​

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐