Java 集合抛出或不抛出';t根据集合的内容抛出ConcurrentModificationException
下面的Java代码抛出了一个Java 集合抛出或不抛出';t根据集合的内容抛出ConcurrentModificationException,java,concurrentmodification,Java,Concurrentmodification,下面的Java代码抛出了一个ConcurrentModificationException,正如预期的那样: public class Evil { public static void main(String[] args) { Collection<String> c = new ArrayList<String>(); c.add("lalala"); c.add("sososo"); c.ad
ConcurrentModificationException
,正如预期的那样:
public class Evil
{
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
c.add("lalala");
c.add("sososo");
c.add("ahaaha");
removeLalala(c);
System.err.println(c);
}
private static void removeLalala(Collection<String> c)
{
for (Iterator<String> i = c.iterator(); i.hasNext();) {
String s = i.next();
if(s.equals("lalala")) {
c.remove(s);
}
}
}
}
这将打印输出“[lalala]”。当第一个示例抛出时,为什么第二个示例不抛出一个
ConcurrentModificationException
?您应该从迭代器中删除(i)而不是直接从集合中删除(c)
试试这个:
for (Iterator<String> i = c.iterator(); i.hasNext();) {
String s = i.next();
if(s.equals("lalala")) {
i.remove(); //note here, changing c to i with no parameter.
}
}
for(迭代器i=c.Iterator();i.hasNext();){
字符串s=i.next();
如果(s.equals(“lalala”)){
i、 remove();//注意这里,将c更改为i,不带参数。
}
}
编辑:
第一个try抛出异常而第二个try没有抛出异常的原因仅仅是因为集合中的元素数量
Collection<String> c = new ArrayList<String>();
c.add("lalala");
c.add("lalala");
removeLalala(c);
System.err.println(c);
因为第一个将多次通过循环,而第二个将只迭代一次。因此,它没有机会抛出异常简短回答
因为不能保证迭代器的快速失败行为
长话短说
之所以出现此异常,是因为除了通过迭代器之外,在对集合进行迭代时无法对其进行操作
坏:
// we're using iterator
for (Iterator<String> i = c.iterator(); i.hasNext();) {
// here, the collection will check it hasn't been modified (in effort to fail fast)
String s = i.next();
if(s.equals("lalala")) {
// s is removed from the collection and the collection will take note it was modified
c.remove(s);
}
}
// we're using iterator
for (Iterator<String> i = c.iterator(); i.hasNext();) {
// here, the collection will check it hasn't been modified (in effort to fail fast)
String s = i.next();
if(s.equals("lalala")) {
// s is removed from the collection through iterator, so the iterator knows the collection changed and can resume the iteration
i.remove();
}
}
//我们正在使用迭代器
对于(迭代器i=c.Iterator();i.hasNext();){
//在此,集合将检查它是否未被修改(努力快速失败)
字符串s=i.next();
如果(s.equals(“lalala”)){
//s已从集合中删除,集合将注意到它已被修改
c、 移除(s);
}
}
好:
// we're using iterator
for (Iterator<String> i = c.iterator(); i.hasNext();) {
// here, the collection will check it hasn't been modified (in effort to fail fast)
String s = i.next();
if(s.equals("lalala")) {
// s is removed from the collection and the collection will take note it was modified
c.remove(s);
}
}
// we're using iterator
for (Iterator<String> i = c.iterator(); i.hasNext();) {
// here, the collection will check it hasn't been modified (in effort to fail fast)
String s = i.next();
if(s.equals("lalala")) {
// s is removed from the collection through iterator, so the iterator knows the collection changed and can resume the iteration
i.remove();
}
}
//我们正在使用迭代器
对于(迭代器i=c.Iterator();i.hasNext();){
//在此,集合将检查它是否未被修改(努力快速失败)
字符串s=i.next();
如果(s.equals(“lalala”)){
//s通过迭代器从集合中移除,因此迭代器知道集合已更改,并可以恢复迭代
i、 删除();
}
}
现在转到“为什么”:在上面的代码中,注意修改检查是如何执行的——删除将集合标记为已修改,下一次迭代将检查是否有任何修改,如果检测到集合已更改,则将失败。另一件重要的事情是,ArrayList
(不确定其他集合)不会在hasNext()
中检查修改
因此,可能会发生两件奇怪的事情:
- 如果在迭代时删除最后一个元素,则不会抛出任何内容
- 这是因为没有“next”元素,所以迭代在到达修改检查代码之前结束
- 如果删除倒数第二个元素,
ArrayList.hasNext()
实际上也会返回false
,因为迭代器的当前索引现在指向最后一个元素(前倒数第二个)。
- 因此,即使在这种情况下,删除后也没有“下一个”元素
请注意,这一切都符合:
请注意,无法保证迭代器的快速失效行为,因为一般来说,在存在非同步并发修改的情况下,不可能做出任何硬保证。快速失败迭代器会尽最大努力抛出ConcurrentModificationException。因此,编写依赖于此异常的正确性的程序是错误的:迭代器的快速失败行为应该只用于检测bug
编辑以添加:
提供一些信息,说明为什么在hasNext()
中不执行并发修改检查,而只在next()
中执行并发修改检查。从其他答案中,您知道在迭代集合时删除集合中元素的正确方法。
我在这里对基本问题进行了解释。你的问题的答案在下面的堆栈跟踪中
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at com.ii4sm.controller.Evil.removeLalala(Evil.java:23)
at com.ii4sm.controller.Evil.main(Evil.java:17)
在stacktrace中,i.next()代码>行抛出错误。但是当集合中只有两个元素时
Collection<String> c = new ArrayList<String>();
c.add("lalala");
c.add("lalala");
removeLalala(c);
System.err.println(c);
Collection c=newarraylist();
c、 添加(“拉拉”);
c、 添加(“拉拉”);
移除维拉拉(c);
系统错误println(c);
当删除第一个异常时,i.hasNext()
返回false,并且从不执行i.next()
来抛出异常如果使用“for each”循环浏览列表,则无法从列表中删除
无法从正在迭代的集合中删除项。您可以通过显式使用迭代器并删除其中的项来解决这个问题。您可以使用迭代器
如果使用以下代码,则不会出现任何异常:
private static void removeLalala(Collection<String> c)
{
/*for (Iterator<String> i = c.iterator(); i.hasNext();) {
String s = i.next();
if(s.equals("lalala")) {
c.remove(s);
}
}*/
Iterator<String> it = c.iterator();
while (it.hasNext()) {
String st = it.next();
if (st.equals("lalala")) {
it.remove();
}
}
}
private static void removeLalala(集合c)
{
/*for(迭代器i=c.Iterator();i.hasNext();){
字符串s=i.next();
如果(s.equals(“lalala”)){
c、 移除(s);
}
}*/
迭代器it=c.Iterator();
while(it.hasNext()){
字符串st=it.next();
如果(标准值等于(“拉拉”)){
it.remove();
}
}
}
如果查看ArrayList
迭代器(私有嵌套类Itr
)的源代码,您将看到代码中的缺陷
代码应该是快速失败的,这是在迭代器内部通过调用checkForComodification()
完成的,但是hasNext()
不会进行该调用,可能是出于性能原因
而hasNext()
只是:
public boolean hasNext() {
return cursor != size;
}
这意味着,当您位于列表的第二个最后一个元素上,然后删除一个元素(任何元素)时,大小将减小,hasNext()
认为您位于最后一个元素上(您不是),并返回false
,跳过最后一个元素的迭代,不会出错
哎呀 在这两种情况下,删除功能是相同的。我的问题是为什么在第二种情况下它没有给出错误,但在第一种情况下它给出错误?@RaviKuldeep我已经更新了我的answ