Listの参照中の編集
Listはスレッドセーフではないので、マルチスレッドでなくても参照中に編集するとjava.util.ConcurrentModificationException
が発生することがあります。
しかし、以外に発生する場合としない場合があるので、記載します。
拡張for文を利用する場合です。
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
for(String str : list){
if("c".equals(str)) {
list.remove("c");
}else {
System.out.println(str);
}
}
上記の場合は
a
b
と正常終了します。
c
が削除されてずれるため、最後に参照されないのでjava.util.ConcurrentModificationException
は発生しません。
次にListに1つ要素を足した場合です。
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
for(String str : list){
if("c".equals(str)) {
list.remove("c");
}else {
System.out.println(str);
}
}
この場合はe
を参照する際にjava.util.ConcurrentModificationException
が発生します。
結果はこんな感じ。
a
b
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1042)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:996)
・・・・
c
が削除されますが、ずれて最後のe
が参照されるためNGなのです。
次に普通にfor文を利用した場合です。
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
for(int i = 0; i < list.size(); i++){
String str = list.get(i);
if("c".equals(str)) {
list.remove("c");
}else {
System.out.println(str);
}
}
こちらは正常に動き結果は以下の通りになります。
a
b
e
参照と編集が同時には行われていないからですかね。
次にforEach
を利用した場合です。
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.forEach(str ->{
if("c".equals(str)) {
list.remove("c");
}else {
System.out.println(str);
}
});
こちらはc
を削除した瞬間にjava.util.ConcurrentModificationException
が発生します。
結果はこんな感じ。
a
b
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList.forEach(ArrayList.java:1542)
・・・
色々なパターンがあるので、for文を回してlist自体を削除するのが、あまりよくないのかもしれませんね。
新しいListを作ってそちらに格納するか、
list.romve("c");
や条件が複雑ならlist.removeIf(s -> "c".equals(s));
などを利用するのがよいですかね。
作成日:2021-08-13 更新日:2021-08-13