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()));
}
}
虽然还可以进一步简化。然而,这是解决此类问题的通用编程方法 - 虽然有一些变化通过集合重新运行并修改直到没有变化。
使用 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()));
}
}
虽然还可以进一步简化。然而,这是解决此类问题的通用编程方法 - 虽然有一些变化通过集合重新运行并修改直到没有变化。