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

标签 java list add listiterator

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

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

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

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

关于Java:在循环中修改列表时避免使用 ListIterator 并发修改异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44073612/

相关文章:

java - 调试 JBehave 场景

java - 如果未使用所有依赖项 jar 进行编译,上传到 Apache Livy 的应用程序将失败

java - 在 Hibernate 中连接到多个数据库

python - 通过在列表前面添加反斜杠来转义列表中的保留字符

使用 jersey 的 REST 服务输出中的 java.util.Map

python - 映射字典和列表 python

Android:如何以编程方式添加按钮以查看

javascript - 将 JavaScript 对象添加到 JavaScript 对象

c++ - 我的程序可以在我的电脑上运行,但不能在 CodeEval 上运行

c# - 如何知道这是循环的最后一次迭代?