【问题标题】:ConcurrentModificationException whilst removing element using nested for-each使用嵌套的 for-each 删除元素时出现 ConcurrentModificationException
【发布时间】:2020-09-02 14:42:46
【问题描述】:

CSV 输入文件。

EmployeeID,FirstName,LastName,Dept
1, John, Smith, Maintenance 
1, John, Smith, Engineering 
1, John, Smith, Transport 

我从 CSV 文件中读取每一行并创建一个员工对象,然后将其存储到员工的 ArrayList 中。

在对员工进行排序之前:

employeeID='1', firstName='John', lastName='Smith', department='Maintenance'}
employeeID='1', firstName='John', lastName='Smith', department='Engineering'}
employeeID='1', firstName='John', lastName='Smith', department='Transport'}

文件处理完成后,我开始对员工的文件进行排序。

排序后的期望输出:

employeeID='1', firstName='John', lastName='Smith', department='Maintenance, Engineering, Transport'}

使用 For-Each 和嵌套的 For-Each,我比较 EmployeeID,如果找到匹配项,我将副本的部门附加到原始副本并删除副本。显然,当我修改列表时,我正在迭代,然后我得到一个 ConcurrentModificationException。

Exception in thread "main" java.util.ConcurrentModificationException
    at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1042)
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:996)
    at com.gillott.csvtomap.CsvToMap.employeeSorter(CsvToMap.java:73)
    at com.gillott.csvtomap.CsvToMap.main(CsvToMap.java:34)

我在网上看到对正在迭代的集合进行修改的建议是使用迭代器,我已经实现了迭代器但仍然得到相同的结果,这是因为我使用的是嵌套的 For -每个?请参阅 For-Each 实现和 For-Each 与 Iterator 实现。

  1. For循环示例:

    for (Employee emp1: employees) {
    
        for (Employee emp2: employees){
    
            if(emp1.getEmployeeID().equals(emp2.getEmployeeID())){
    
                emp1.setdepartment(emp1.getdepartment() + ", " + emp2.getdepartment());
                System.out.println("Duplicate found for Employee ID: " + emp1.getEmployeeID());
                employees.remove(emp2); //CME Exception Throw here.
            }
        }
    }
    
  2. 迭代器 For-Loop 示例:

    for (Iterator<Employee> employeeIterator1 = employees.iterator(); employeeIterator1.hasNext();) {
        Employee emp1 = employeeIterator1.next();
    
        for (Iterator<Employee> employeeIterator2 = employees.iterator(); employeeIterator2.hasNext();){
            Employee emp2 = employeeIterator2.next();
    
            if(emp1.getEmployeeID().equals(emp2.getEmployeeID())){
    
                emp1.setdepartment(emp1.getdepartment() + ", " + emp2.getdepartment());
                System.out.println("Duplicate found for Employee ID: " + emp1.getEmployeeID());
                employeeIterator1.remove();
            }
        }
    }
    

我的迭代器实现可能不正确,这是第一次直接使用迭代器。

【问题讨论】:

    标签: java loops foreach concurrency iterator


    【解决方案1】:

    按 id 对员工进行分组,并使用下游收集器合并实例:

    Map<Integer, Employee> map =
        employees.stream()
            .collect(groupingBy(
                Employee::getId,
                reducing((a, b) -> a.setdepartment(a.getdepartment() + "," + b.getdepartment()));
    

    然后保留employees中的实例,它们是此映射中的值:

    employees.retainAll(map.values());
    

    如果您愿意,您可以在一个语句中完成所有这些操作。

    【讨论】:

    • 嗨,安迪,当减少方法时,您的答案(a, b) 中有 2 个参数。 ab 而不是 Employee 类型,所以我不能调用 a.setdepartment(a.getdepatment() + "," + b.getdepartment())); 你能解释一下我如何制作这些 Employee 类型的参数吗?
    • @Bradley 您可以声明显式类型:(Employee a, Employee b)
    【解决方案2】:

    因为您循环遍历数组并将其删除,所以您可以使用另一个包含重复项的列表并从中删除,或者使用任何并发列表,例如 CopyOnWriteArrayList

    您可以使用 Set 删除重复项,并覆盖 Employee 类中的 equals()hashCode() 并确定要在 hashCode 和 equals 中使用哪些属性

    Set<Employee> unique = new HashSet<>(employees);
    

    更多信息:Concurrent collection Oracle doc

    【讨论】:

    • 我已经覆盖了我的员工类中的equalshashCode()。这将如何将部门从重复的员工 ID 复制到唯一版本?
    • 还没有,如果我在员工类中创建一个部门列表,那么当我找到匹配项时,我可以添加新部门并返回唯一的集合?
    【解决方案3】:

    理想情况下,如果实施得当,迭代器方式应该可以工作。但是,您在“迭代器 for-loop”案例中编写的代码的问题是您正在为同一个列表(员工)初始化两个迭代器,即两个迭代器

    迭代器employeeIterator1 = employees.iterator();

    迭代器employeeIterator2 = employees.iterator();

    两个迭代器都是为同一个列表创建的。因此,employeeIterator2 会抛出 ConcurrentModificationException,这表明列表已被更改(除了迭代器自己的 remove 方法,在您的情况下是 'employeeIterator1') 在 Iterator employeeIterator2 创建之后。

    对于您的场景,您可以创建一个虚拟/临时列表,它是原始员工列表的副本,并在创建 iterator2 时使用该新列表。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-06-03
      • 1970-01-01
      • 2018-01-09
      • 1970-01-01
      • 1970-01-01
      • 2019-09-11
      • 2023-03-10
      • 2013-03-02
      相关资源
      最近更新 更多