次のコードを見てください。
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
strings.add("apple");
strings.add("banana");
strings.add("cherry");
for (String s : strings) {
System.out.print(s + " ");
strings.remove(s);
}
}
}
このプログラムを実行すると、どのような出力が得られますか?また、その理由を説明してください。
選択肢
A) apple banana cherry
B) apple cherry
C) apple
D) 例外が発生する
答えと解説
A) apple banana cherry
B) apple cherry
C) apple
D) 例外が発生する
プログラムの流れ
- リストの初期化:javaコードをコピーする
List<String> strings = new ArrayList<>(); strings.add("apple"); strings.add("banana"); strings.add("cherry");
この部分で、strings
というArrayList
に"apple"
,"banana"
,"cherry"
の3つの文字列が追加されています。 - for-eachループの開始:javaコードをコピーする
for (String s : strings) { System.out.print(s + " "); strings.remove(s); }
このループは、strings
リスト内の各要素に対して反復処理を行います。
ループ内での要素削除
ループの中で strings.remove(s);
を実行しています。これにより、現在処理している要素がリストから削除されます。これが問題の核心です。
ConcurrentModificationException
- 構造の変更:
for-each
ループは、リストの「構造」を変更することを許可しません。具体的には、要素を追加したり削除したりすると、イテレータが保持している状態と一致しなくなるため、ConcurrentModificationException
が発生します。
- イテレータの仕組み:
for-each
ループは内部的にイテレータを使用しており、リストの構造が変更されるとイテレータはそれに対応できなくなります。この場合、Javaは安全性を確保するために例外をスローします。
例外の発生タイミング
- 最初のループで
"apple"
が処理されます。System.out.print(s + " ");
が実行され、"apple "
が出力されます。 - 次に
strings.remove(s);
により"apple"
がリストから削除されます。この時点でリストの内容は["banana", "cherry"]
になります。 - 次に
"banana"
に進むと、イテレータはもはや削除された要素に対するアクセスができないため、ConcurrentModificationException
が発生します。
まとめ
この問題は、コレクションを反復処理する際に注意が必要な点を示しています。要素を削除したり追加したりする必要がある場合は、以下の方法を考慮すべきです:
- Iteratorを使う:javaコードをコピーする
Iterator<String> iterator = strings.iterator(); while (iterator.hasNext()) { String s = iterator.next(); System.out.print(s + " "); iterator.remove(); // Iteratorのremoveメソッドを使用 }
- 別のコレクションにコピー:
- 反復処理中に要素を削除する場合、別のリストに対象の要素を収集し、処理後に一括削除する方法もあります。
このように、コレクションの操作にはその特性を理解して行うことが重要です。
次の問題へ SE17_C-01