Java:避免在循环中修改列表时使用 ListIterator 出现并发修改异常

Java: avoid Concurrent Modification Exception with ListIterator while modifying a List in a loop

使用 Java,我有一个列表要检查,如果通过循环检索到的子列表之一满足某些条件,则必须即时替换它,因为在下一个循环中我必须重复更改列表中的算法。当然,要替换的子列表和要添加的子列表可以有不同的长度。

我正在使用 ListIterator,因为使用普通的 List 方法添加和删除不能保证结果。但即使在这里,在主 for 循环内的第一个 for 循环(在其第二次迭代)的开始处也会抛出一个 ConcurrentModificationException,我真的想不出另一种方法来执行该算法。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;

public class Dummy {
    public static void main(String[] args) {
        List<String> nodes = new ArrayList<>(Arrays.asList(
                "1", "2", "3", "4", "5", "6", "7"));
        ListIterator<String> nodesIterator = nodes.listIterator();
        while (nodesIterator.hasNext()) {
            nodesIterator.next();
            List<String> nodesToCheck = nodes.subList(nodesIterator
                            .previousIndex(),
                    nodesIterator.previousIndex() + 3);
            if (nodesToCheck.equals(Arrays.asList("3", "4", "5"))) {
                List<String> nodesToReplace = new ArrayList<String>(
                        Arrays.asList("11", "11", "00", "11"));
                for (String n : nodesToCheck) {
                    //ConcurrentModificationException thrown here at 
                    // beginning of second iteration
                    nodesIterator.remove();
                    nodesIterator.next();
                }
                for (String n : nodesToReplace) {
                    nodesIterator.add(n);
                }
            }
        }
    }
}

非常感谢任何帮助,谢谢 ;)

您正在修改初始节点列表,因此抛出异常是很自然的。您应该遍历节点列表的副本。

这是一个可重现问题的可运行示例(我在编辑 OP 之前写的):

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;

public class Main {

    public static void main(String[] args) {
        int number = 2;
        List<String> nodes = new ArrayList<>(Arrays.asList("Hello",
            "World!", "How", "Are", "You"));
        ListIterator<String> nodesIterator = nodes.listIterator();
        while (nodesIterator.hasNext()) {
            nodesIterator.next();
            int fromIndex = nodesIterator.previousIndex();
            List<String> nodesToCheck = nodes.subList(fromIndex,fromIndex + number);
            if (nodesToCheck.contains("Hello")) {
                for (String n : nodesToCheck) { //ConcurrentModificationException 
                    // thrown here at beginning of second iteration
                    nodesIterator.remove();
                    nodesIterator.next();
                }
                List<String> nodesToReplace = new ArrayList<>(
                    Arrays.asList("replace"));
                for (String n : nodesToReplace) {
                    nodesIterator.add(n);
                }
            }
        }
    }
}

一种快速而肮脏的修补方法(在更复杂的设计中可能是必需的)是:

public class Main {

    public static void main(String[] args) {
        int number = 2;
        List<String> nodes = new ArrayList<>(Arrays.asList("Hello", "World!",
                "How", "Are", "You"));
        boolean change = true;
        while (change) {
            List<String> copy = new ArrayList<>(nodes);
            ListIterator<String> nodesIterator = copy.listIterator();
            while (nodesIterator.hasNext()) {
                nodesIterator.next();
                int fromIndex = nodesIterator.previousIndex();
                List<String> nodesToCheck = nodes.subList(fromIndex, Math.min
                        (fromIndex + number, nodes.size()));
                if ((nodesToCheck.equals(Arrays.asList("How", "Are")) ||
                        nodesToCheck.equals(Arrays.asList("Hello", "World!")))) {
                    for (String n : nodesToCheck) {
                        //ConcurrentModificationException thrown here
                        // at beginning of second iteration
                        nodesIterator.remove();
                        if (nodesIterator.hasNext()) {
                            nodesIterator.next();
                        }
                    }
                    nodesIterator.previous();
                    List<String> nodesToReplace = new ArrayList<>(Arrays.asList
                            ("replace"));
                    for (String n : nodesToReplace) {
                        nodesIterator.add(n);
                    }
                    nodes = copy;
                    change = true;
                    break;
                } else change = false;
            }
        }
        System.out.println(Arrays.toString(nodes.toArray()));
    }
}

虽然还可以进一步简化。然而,这是解决此类问题的通用编程方法 - 虽然有一些变化通过集合重新运行并修改直到没有变化。