【Java】关于ConcurrentModificationException,适合初学者的讲解【备考Java Silver】
首先
-
- この記事は駆け出しエンジニアが書いています。間違いがありましたら遠慮なくご指摘いただけると幸いです。
- この記事はJavaのArrayListにおけるConcurrentModificationExceptionについて超ざっくりと解説します
这篇文章的受众对象
-
- Java Silver勉強中で、どういう場合に発生するのか確認したい方
- Javaでシングルスレッドのコードを書いていたらConcurrentModificationExceptionが出たのでとりあえず解決したい方。
只写了简单易懂的内容,省略了详细解释,即使是初学者也能理解。
关于ConcurrentModificationException
我还是一个新手作者,但在做Java Se8 Silver的问题集时遇到了这样的问题。
子: 你吃饭了吗?
父: 吃了,你呢?
子: 我还没吃。
//配列を宣言し「ABCDE」を要素として配列に格納
List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");
//拡張for文でリストの中身を一つづつ取り出し、
//「B」であればリストの中身の「B」を削除
//それ以外はコンソールに表示させる
for (String string : list) {
if(string.equals("B")) {
list.remove(string);
} else {
System.out.println(string);
}
}
运行这个会引发异常。
実行結果
A
Exception in thread "main" java.util.ConcurrentModificationException
我认为通过只显示A,就能够看出在remove方法中发生了异常。
为什么会发生?
总的来说,这是由于在扩展for循环中执行了”在提取元素时执行remove方法”的操作。这是为了在多线程环境下,当一个线程正在提取元素时,另一个线程不会从同一个列表中删除元素而引发的异常。但是,在这种单线程环境下,也可能发生这种异常。由于初学者很少遇到这种情况,所以投入的时间和精力相对较少。如果你不太明白,只要大致知道这种异常存在就足够了。
【Java Silver对策】什么时候不会发生呢?
总结起来就是,如果要删除列表中倒数第二个元素,则不会出现这种情况。
有一个类似于先前提到的问题的例题。
List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");
for (String string : list) {
if(string.equals("D")) {
list.remove(string);
} else {
System.out.println(string);
}
}
只是将扩展for循环中的equals方法的内容更改为”D”的代码。关于这个执行结果是什么呢?
実行結果
A
B
C
这样就不会发生异常。(为什么不显示”E”是另一个问题,本文不涉及此)
只需一個選項,以下是從中文本地化的角度重新解釋:如果從列表元素”ABCDE”中移除倒數第二個元素”D”,將不會發生異常。換句話說,”A”、”B”、”C”、”E”中的每個元素都將引發異常。
我们要记住,这也是这样的事情。
为了避免发生ConcurrentModificationException异常
在实际编写代码时可能不经常遇到这种情况,但为了应对这种情况,我想到了一些解决办法并在这里进行记录。
通常情况下,我认为解决办法1就足够了,但如果确实有需要在提取元素的同时进行删除的情况,建议参考”解决办法3″和”解决办法4″。
解决方案1-提前删除
List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");
list.remove("B");
for (String string : list) {
System.out.println(string);
}
実行結果
A
C
D
E
这个非常简单,但在增强型for循环之前删除元素。
我认为只有那些非常想要在提取元素的同时删除它的人才需要这样做,其他人应该用这个就可以了。
从以下的解决方案到省略列表元素的添加
方法2- 使用常规的for循环(不推荐)
for (int i = 0; i < list.size(); i++) {
if(list.get(i).equals("B")) {
list.remove("B");
} else {
System.out.println(list.get(i));
}
}
実行結果
A
D
E
在循环处理中,如果想要删除元素,仍然可以使用常规的for循环而不会引发异常。然而,正如执行结果所示,由于元素的顺序变化,执行结果往往变得不符合预期。因此,不推荐使用这种方式。
使用迭代器是第三种应对措施。
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String string = iterator.next();
if(string.equals("B")) {
iterator.remove();
} else {
System.out.println(string);
}
}
実行結果
A
C
D
E
也许省略具体迭代器的细节,但迭代器默认假设”在提取元素的同时进行删除操作”,因此可以通过使用迭代器来避免异常。如果确实需要在提取元素的同时进行删除操作,我认为这将是一个解决方案。
方案4 – 使用removeIf方法
list.removeIf((String string) ->{
if(string.equals("B")) {
return true;
} else {
System.out.println(string);
return false;
}
});
実行結果
A
C
D
E
如上所述,如果我们想要在提取元素的同时删除它们,只能使用迭代器。但是,removeIf方法也可以帮助我们提取迭代器。它使用Lambda表达式指定条件,如果条件为true,则删除该元素。相比在代码中调用迭代器,这种方法会使代码更清晰一些,因此可能更易于使用。然而,Lambda表达式(->)对于初学者来说可能不太容易理解,因此在使用时最好写上注释。
尽管是备考时的一点提醒,但参加Java Silver考试的考生应该牢记removeIf方法是考试范围内的内容,掌握它并没有任何损失。
最后
我在之前解释了关于ConcurrentModificationException的情况,但对于初学者来说,这不是一个常见的错误,所以当他们在考试或者编写代码时遇到这个错误时可能会感到困惑。
当我查询这个错误时,我也发现没有找到以日语正确解释的资料,所以我的困惑相当大。
然而,由于这个错误不常见,我认为在理解其内部原理之前,只需要在这篇文章中“记住这样的情况”可能更简便一些。
如果这篇文章对大家有所帮助,我会非常高兴。