次のように、ArrayListをトラバースして、条件に従って要素を削除する必要がある場合があります。
public static void main(String[] args) {
String fff = new String("fff");
String a=new String();
ArrayList<String> list= new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
for(String s:list) {
if("aa".equals(s)) {
list.remove("aa");
}
}
}
しかし、これは次のエラーを報告し
ます:生成されたクラスファイルの逆コンパイル結果を見てみましょう:
public static void main(String[] var0) {
new String("fff");
new String();
ArrayList var3 = new ArrayList();
var3.add("aa");
var3.add("bb");
var3.add("cc");
Iterator var4 = var3.iterator();
while(var4.hasNext()) {
String var5 = (String)var4.next();
if ("aa".equals(var5)) {
var3.remove("aa");
}
}
}
ここでのトラバースでは、ArrayListによって実装されたイテレーターのhasNext()およびnext()メソッドを使用していますが、削除にはArrayListのremove(Object o)メソッドを使用しています。このように、イテレータはArrayList内の要素の変更を認識できません。たとえば、ArrayList内の要素が削除され、その後ろの要素が1つ前に移動します。元のイテレータの位置にある要素は削除され、後ろの要素に置き換えられますが、イテレータはそうではありません次の反復では、この要素がトラバースされたと見なされ、直接スキップされます。
この動作は、ArrayListで監視されます
。ModCountとExpectedModCountが等しく、ModCountが実行されるたびにArrayList.remove()がインクリメントされることがわかります。
イテレーターは、ModCountとExpectedModCountが等しいことを確認します。
次に、ConcurrentModificationExceptionをスローします
正しいアプローチ:
iterator.remove()がここで行うことを見てみましょう
。実際、list.remove(Object o)を呼び出すときに、「expectedModCount = modCount;」という文を追加すると、それらの値は次の反復で等しくなります。
補足:
javacを使用してjavaを.classファイル(javac [-encoding utf-8] javaName.java)にコンパイルし、それを開発ツールで開いたり、.classファイルの逆コンパイルされたコンテンツを表示したり、開発ツールで直接表示したりできます。クラスファイル(逆コンパイルされたファイル)を表示します。一部の「非表示」メソッドの特定の実装を確認するには、
次のようにします。for(String s:list){
if( "aa" .equals(s)){
list.remove( "aa");
}
}
String var5;
while(var4.hasNext()){
var5 =(String)var4.next();
if( "aa" .equals(var5)){
var3.remove( "aa");
注:jdkを使用する場合javap命令、つまりjavap classNameを使用すると、メソッド名のみがソースコードの元の外観に表示される場合があります。