Java 单线程不可修改列表中的ConcurrentModificationException

Java 单线程不可修改列表中的ConcurrentModificationException,java,collections,Java,Collections,我有一个单线程应用程序,它在一个巨大的树结构上迭代,其中子对象存储在一个列表中。迭代器始终在不可修改的列表上运行: public List<HierarchyNode> getChildren() { return Collections.unmodifiableList(children); } 因此Collection.unmodifiableList并不是真正的线程安全的。这是因为它创建了基础列表的不可修改视图。但是,如果在迭代视图时修改了基础列表

我有一个单线程应用程序,它在一个巨大的树结构上迭代,其中子对象存储在一个列表中。迭代器始终在不可修改的列表上运行:

public List<HierarchyNode> getChildren() {  
        return Collections.unmodifiableList(children);  
}  
因此Collection.unmodifiableList并不是真正的线程安全的。这是因为它创建了基础列表的不可修改视图。但是,如果在迭代视图时修改了基础列表,则会得到CME。请记住,CME不需要由单独的线程引起。如果我执行以下操作,我将获得CME:

 for (String e : myList){
     myList.remove(5); // throws CME
 }
更好的选择是Guava's,它创建传递列表的不可变副本

如需澄清,请将张贴的代码替换为:

 public List<HierarchyNode> getChildren() {  
        return ImmutableList.copyOf(children);  
    }
从该方法返回的列表保证从不抛出CME

更新:

如果你仍在试图找出代码中的原因,请考虑以下内容:

我指的是传递给unmodifiableList的可修改列表,子列表是否可以修改? 调用getChildren的类中是否有任何一个导致以可能更新列表的方式调用保存可修改列表的类? 或者这些类中是否有一个迭代器实例被多次使用? 因此Collection.unmodifiableList并不是真正的线程安全的。这是因为它创建了基础列表的不可修改视图。但是,如果在迭代视图时修改了基础列表,则会得到CME。请记住,CME不需要由单独的线程引起。如果我执行以下操作,我将获得CME:

 for (String e : myList){
     myList.remove(5); // throws CME
 }
更好的选择是Guava's,它创建传递列表的不可变副本

如需澄清,请将张贴的代码替换为:

 public List<HierarchyNode> getChildren() {  
        return ImmutableList.copyOf(children);  
    }
从该方法返回的列表保证从不抛出CME

更新:

如果你仍在试图找出代码中的原因,请考虑以下内容:

我指的是传递给unmodifiableList的可修改列表,子列表是否可以修改? 调用getChildren的类中是否有任何一个导致以可能更新列表的方式调用保存可修改列表的类? 或者这些类中是否有一个迭代器实例被多次使用?
你不能修改一个集合,然后在它上面使用一个已有的迭代器-你是在某处试图修改这个列表,这里我指的是原始列表吗

发件人:

请注意,此异常并不总是表示对象具有 被另一个线程同时修改


你不能修改一个集合,然后在它上面使用一个已有的迭代器-你是在某处试图修改这个列表,这里我指的是原始列表吗

发件人:

请注意,此异常并不总是表示对象具有 被另一个线程同时修改

只有持有该列表的类的构造函数才可能修改该列表

在这种情况下,您根本不需要修改它。我建议你让它永远不变,你不会得到这个问题

在构造函数中,您可以执行以下操作

List<HierarchyNode> children = new ArrayList<>();
// create/modify collection.

this.children = Collections.immutableList(children);
印刷品

Before modification
words: [hello, world]
unmodifiable: [hello, world]
copy: [hello, world]

After modification
words: [world, hi]
unmodifiable: [world, hi]
copy: [hello, world]
在不添加新库的情况下,解决方案是在返回集合之前复制集合

只有持有该列表的类的构造函数才可能修改该列表

在这种情况下,您根本不需要修改它。我建议你让它永远不变,你不会得到这个问题

在构造函数中,您可以执行以下操作

List<HierarchyNode> children = new ArrayList<>();
// create/modify collection.

this.children = Collections.immutableList(children);
印刷品

Before modification
words: [hello, world]
unmodifiable: [hello, world]
copy: [hello, world]

After modification
words: [world, hi]
unmodifiable: [world, hi]
copy: [hello, world]

在不添加新库的情况下,解决方案是在返回集合之前复制集合。

您可能试图在迭代中修改列表,如下所示:

Iterator iter = list.iterator();
while (iter.hasNext()) {
  if (someCondition)
    list.remove(someObject);
}
相反,这是您应该做的:

for (int i = 0; i < list.size(); i++) {
 if (someCondition)
    list.remove(i--);
 }

如果逻辑允许,可以使用CopyOnWriteArrayList。每次修改列表时,此实现都会为您提供一个新的列表副本。这对于对列表进行大量迭代但很少修改,并且给定迭代不需要反映最近对列表的修改的情况非常有用。我在dispatcher/subscriber应用程序中经常使用它,它在性能方面非常有效。

您可能正在尝试在迭代中修改列表,如下所示:

Iterator iter = list.iterator();
while (iter.hasNext()) {
  if (someCondition)
    list.remove(someObject);
}
相反,这是您应该做的:

for (int i = 0; i < list.size(); i++) {
 if (someCondition)
    list.remove(i--);
 }

如果逻辑允许,可以使用CopyOnWriteArrayList。每次修改列表时,此实现都会为您提供一个新的列表副本。这对于对列表进行大量迭代但很少修改,并且给定迭代不需要反映最近对列表的修改的情况非常有用。我在dispatcher/subscriber应用程序中经常使用它,它在性能方面非常有效。

对不起,各位,你们大多数人都是对的,答案很简单,列表并不是真的不可修改的。。。同一类的所有对象都不使用getChildren方法,而是使用direc

直接访问私有成员。所以我必须继续在代码中搜索。。。您的评论将有助于

对不起,各位,你们大多数人都是对的,答案很简单,列表并非真的无法修改。。。同一类的所有对象都不使用getChildren方法,而是直接访问私有成员。所以我必须继续在代码中搜索。。。你的评论将有助于

我之前也遇到过同样的问题。你能展示你的迭代代码吗?请写下你的完整堆栈跟踪。这通常是由于在迭代集合时修改集合造成的。我知道,我已经解决了这里问到的其他问题;但是它是不可修改的!!我之前也遇到过同样的问题。你能展示你的迭代代码吗?请写下你完整的堆栈跟踪。这通常是由于在迭代集合时修改集合造成的。我知道,我已经解决了这里提出的其他问题;但是它是不可修改的!!请举例说明你的答案,以便理解。+1另一个解决办法是复制它。返回新的ArrayListchildren;嗯,这是一个解决办法,但它不能解决问题;-。同样,这是单线程的,它是不可修改的…@BennyBarns,正如我的帖子所说,你可以在一个线程中得到一个CME,不可修改并不保证没有CME,因为它创建了一个视图。正如我更新的帖子所说,使用ImmutableList可以保证永远不会抛出CME。如果看不到更多的代码,我们就无法尝试调试您获得CME的原因。但是,ImmutableList将阻止它。您得到的异常显示一定有什么东西修改了该列表。没有其他解释。请用例子详细说明你的答案,以便理解。+1另一个解决办法是复制它。返回新的ArrayListchildren;嗯,这是一个解决办法,但它不能解决问题;-。同样,这是单线程的,它是不可修改的…@BennyBarns,正如我的帖子所说,你可以在一个线程中得到一个CME,不可修改并不保证没有CME,因为它创建了一个视图。正如我更新的帖子所说,使用ImmutableList可以保证永远不会抛出CME。如果看不到更多的代码,我们就无法尝试调试您获得CME的原因。但是,ImmutableList将阻止它。您得到的异常显示一定有什么东西修改了该列表。没有其他解释。如果它是不可修改的,那么我如何修改它;要么这是JVM的问题,要么我对不可修改的理解是完全错误的……这里的不可修改意味着,如果您试图修改它,您将得到一个运行时异常。但我们实际上都是在黑暗中拍摄,因为你没有给我们stacktrace。如果它是不可修改的,那么我如何修改它;要么这是JVM的问题,要么我对不可修改的理解是完全错误的……这里的不可修改意味着,如果您试图修改它,您将得到一个运行时异常。但是我们都只是在黑暗中拍摄,因为你没有给我们stacktrace。我已经检查过了:唯一可能修改这个列表的人是作为成员持有它的类的构造函数。包括子类在内的所有其他类只能通过使用此不可修改列表来访问该列表:在这种情况下,请参阅我关于如何防止修改以及可能检测修改位置的回答。我已经检查了这一点:唯一可能修改此列表的是将其作为成员持有的类的构造函数。所有其他人,包括子类,只能通过使用此不可修改列表来访问列表:在这种情况下,请参阅我关于如何防止修改并可能检测其修改位置的回答。编辑:如果列表确实不可修改,我上面的回答显然不适用……sorryEDIT:如果列表确实不可修改,我上面的回答显然不适用……对不起